diff --git a/.mockery.yaml b/.mockery.yaml index 1fe855a..779a411 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -1,14 +1,24 @@ disable-version-string: true mockname: '{{.InterfaceName}}' filename: '{{.MockName}}.go' -outpkg: mocks +outpkg: mock +with-expecter: true packages: - github.com/sebajax/go-architecture-angrycoders/internal/user: + github.com/sebajax/go-vertical-slice-architecture/internal/user: # place your package-specific config here config: - dir: 'internal/{{.PackageName}}/mocks' + dir: 'internal/{{.PackageName}}/mock' interfaces: # select the interfaces you want mocked UserRepository: # Modify package-level config for this specific interface (if applicable) config: + github.com/sebajax/go-vertical-slice-architecture/internal/product: + # place your package-specific config here + config: + dir: 'internal/{{.PackageName}}/mock' + interfaces: + # select the interfaces you want mocked + ProductRepository: + # Modify package-level config for this specific interface (if applicable) + config: diff --git a/README.md b/README.md index 3e5faaf..fb8843d 100644 --- a/README.md +++ b/README.md @@ -60,10 +60,6 @@ A brief description of the layout: ## 🤜 How to create a new use case (Example) -### Database diagram for the project - -![alt text](./image/db_diagram.png) - ### Internal folder structure for a new domain all folders and files go in internal/product/ ```tree @@ -210,7 +206,7 @@ https://github.com/sebajax/go-vertical-slice-architecture/blob/eb79ccae805d23b6f ```bash # To run unit testing - go test + go test ./... # To run unit testing coverage go test -cover ./... diff --git a/go.mod b/go.mod index 9ae5a03..d778fdb 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.17.0 github.com/jackc/pgx/v5 v5.5.3 github.com/sebajax/go-architecture-angrycoders v0.0.0-20240215234534-8b0c71a519da + github.com/stretchr/testify v1.8.4 go.uber.org/dig v1.17.1 ) @@ -25,6 +26,7 @@ require ( ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -33,12 +35,17 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.9 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.18.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e0d9c9c..554efba 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -50,6 +51,10 @@ github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -75,10 +80,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sebajax/go-architecture-angrycoders v0.0.0-20240215234534-8b0c71a519da h1:Y/IGfCNQJgGc+8nwxL9gnL7FrVPdmxGTXLJe7pxL5z8= github.com/sebajax/go-architecture-angrycoders v0.0.0-20240215234534-8b0c71a519da/go.mod h1:X7kYg0VhvL9alodexKrNZ26vFPF2ZlGrGE2Z538T4/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -114,6 +122,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/product/mock/ProductRepository.go b/internal/product/mock/ProductRepository.go new file mode 100644 index 0000000..6a79065 --- /dev/null +++ b/internal/product/mock/ProductRepository.go @@ -0,0 +1,156 @@ +// Code generated by mockery. DO NOT EDIT. + +package mock + +import ( + product "github.com/sebajax/go-vertical-slice-architecture/internal/product" + mock "github.com/stretchr/testify/mock" +) + +// ProductRepository is an autogenerated mock type for the ProductRepository type +type ProductRepository struct { + mock.Mock +} + +type ProductRepository_Expecter struct { + mock *mock.Mock +} + +func (_m *ProductRepository) EXPECT() *ProductRepository_Expecter { + return &ProductRepository_Expecter{mock: &_m.Mock} +} + +// GetBySku provides a mock function with given fields: sku +func (_m *ProductRepository) GetBySku(sku string) (*product.Product, bool, error) { + ret := _m.Called(sku) + + if len(ret) == 0 { + panic("no return value specified for GetBySku") + } + + var r0 *product.Product + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func(string) (*product.Product, bool, error)); ok { + return rf(sku) + } + if rf, ok := ret.Get(0).(func(string) *product.Product); ok { + r0 = rf(sku) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*product.Product) + } + } + + if rf, ok := ret.Get(1).(func(string) bool); ok { + r1 = rf(sku) + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(sku) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ProductRepository_GetBySku_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBySku' +type ProductRepository_GetBySku_Call struct { + *mock.Call +} + +// GetBySku is a helper method to define mock.On call +// - sku string +func (_e *ProductRepository_Expecter) GetBySku(sku interface{}) *ProductRepository_GetBySku_Call { + return &ProductRepository_GetBySku_Call{Call: _e.mock.On("GetBySku", sku)} +} + +func (_c *ProductRepository_GetBySku_Call) Run(run func(sku string)) *ProductRepository_GetBySku_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *ProductRepository_GetBySku_Call) Return(_a0 *product.Product, _a1 bool, _a2 error) *ProductRepository_GetBySku_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *ProductRepository_GetBySku_Call) RunAndReturn(run func(string) (*product.Product, bool, error)) *ProductRepository_GetBySku_Call { + _c.Call.Return(run) + return _c +} + +// Save provides a mock function with given fields: p +func (_m *ProductRepository) Save(p *product.Product) (int64, error) { + ret := _m.Called(p) + + if len(ret) == 0 { + panic("no return value specified for Save") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(*product.Product) (int64, error)); ok { + return rf(p) + } + if rf, ok := ret.Get(0).(func(*product.Product) int64); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(*product.Product) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ProductRepository_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save' +type ProductRepository_Save_Call struct { + *mock.Call +} + +// Save is a helper method to define mock.On call +// - p *product.Product +func (_e *ProductRepository_Expecter) Save(p interface{}) *ProductRepository_Save_Call { + return &ProductRepository_Save_Call{Call: _e.mock.On("Save", p)} +} + +func (_c *ProductRepository_Save_Call) Run(run func(p *product.Product)) *ProductRepository_Save_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*product.Product)) + }) + return _c +} + +func (_c *ProductRepository_Save_Call) Return(_a0 int64, _a1 error) *ProductRepository_Save_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ProductRepository_Save_Call) RunAndReturn(run func(*product.Product) (int64, error)) *ProductRepository_Save_Call { + _c.Call.Return(run) + return _c +} + +// NewProductRepository creates a new instance of ProductRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProductRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *ProductRepository { + mock := &ProductRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/product/mock/mockProductRepository.go b/internal/product/mock/mockProductRepository.go deleted file mode 100644 index c41911f..0000000 --- a/internal/product/mock/mockProductRepository.go +++ /dev/null @@ -1,28 +0,0 @@ -package mocks - -import ( - "time" - - "github.com/sebajax/go-vertical-slice-architecture/internal/product" -) - -type mockProductRepository struct{} - -func NewMockProductRepository() product.ProductRepository { - return &mockProductRepository{} -} - -func (mock *mockProductRepository) Save(p *product.Product) (int64, error) { - return 1, nil -} - -func (mock *mockProductRepository) GetBySku(email string) (*product.Product, bool, error) { - return &product.Product{ - Id: 1, - Name: "ps5", - Sku: "123456sony", - Category: product.SmartWatch, - Price: 300.00, - CreatedAt: time.Now(), - }, true, nil -} diff --git a/internal/product/service/createProduct.go b/internal/product/service/createProduct.go index e8f2c93..a0e488a 100644 --- a/internal/product/service/createProduct.go +++ b/internal/product/service/createProduct.go @@ -26,7 +26,7 @@ func (service *CreateProductService) CreateProduct(p *product.Product) (int64, e // check if product sky does not exist and no database error ocurred if err != nil { // database error - log.Fatalln(err) + log.Println(err) err := apperror.InternalServerError() return 0, err } @@ -41,7 +41,7 @@ func (service *CreateProductService) CreateProduct(p *product.Product) (int64, e id, err := service.productRepository.Save(p) if err != nil { // database error - log.Fatalln(err) + log.Println(err) err := apperror.InternalServerError() return 0, err } diff --git a/internal/product/service/createProduct_test.go b/internal/product/service/createProduct_test.go new file mode 100644 index 0000000..31d515c --- /dev/null +++ b/internal/product/service/createProduct_test.go @@ -0,0 +1,97 @@ +package service + +import ( + "errors" + "testing" + + "github.com/sebajax/go-vertical-slice-architecture/internal/product" + "github.com/sebajax/go-vertical-slice-architecture/internal/product/mock" + "github.com/stretchr/testify/assert" +) + +func TestCreateProductService_CreateProduct_Success(t *testing.T) { + var insertedId int64 = 1 + // Create a new Product + p, _ := product.New("test_product", "test_sku", product.Laptop, 1200) + + // Create a mock ProductRepository instance + mockProductRepository := &mock.ProductRepository{} + + // Set expectations on the mock + mockProductRepository.EXPECT().GetBySku(p.Sku).Return(nil, false, nil) + mockProductRepository.EXPECT().Save(p).Return(insertedId, nil) + + // Pass the mock as a dependency + productService := NewCreateProductService(mockProductRepository) + + // Call the method being tested + id, err := productService.CreateProduct(p) + + // Assert the result + assert.NoError(t, err) + assert.Equal(t, insertedId, id) + + // Assert that the method was called exactly once or not called + mockProductRepository.AssertCalled(t, "GetBySku", p.Sku) + mockProductRepository.AssertCalled(t, "Save", p) + + // Assert that the expectations were met + mockProductRepository.AssertExpectations(t) +} + +func TestCreateProductService_CreateProduct_GetBySku_Failure(t *testing.T) { + // Create a new Product + p, _ := product.New("test_product", "test_sku", product.Laptop, 1200) + + // Create a mock ProductRepository instance + mockProductRepository := &mock.ProductRepository{} + + // Set expectations on the mock + mockProductRepository.EXPECT().GetBySku(p.Sku).Return(p, true, nil) + + // Pass the mock as a dependency + productService := NewCreateProductService(mockProductRepository) + + // Call the method being tested + id, err := productService.CreateProduct(p) + + // Assert the result + assert.Error(t, err) + assert.Equal(t, int64(0), id) + + // Assert that the method was called exactly once or not called + mockProductRepository.AssertCalled(t, "GetBySku", p.Sku) + mockProductRepository.AssertNotCalled(t, "Save") + + // Assert that the expectations were met + mockProductRepository.AssertExpectations(t) +} + +func TestCreateProductService_CreateProduct_Save_Failure(t *testing.T) { + // Create a new Product + p, _ := product.New("test_product", "test_sku", product.Laptop, 1200) + + // Create a mock ProductRepository instance + mockProductRepository := &mock.ProductRepository{} + + // Set expectations on the mock + mockProductRepository.EXPECT().GetBySku(p.Sku).Return(nil, false, nil) + mockProductRepository.EXPECT().Save(p).Return(0, errors.New("DB ERROR")) + + // Pass the mock as a dependency + productService := NewCreateProductService(mockProductRepository) + + // Call the method being tested + id, err := productService.CreateProduct(p) + + // Assert the result + assert.Error(t, err) + assert.Equal(t, int64(0), id) + + // Assert that the method was called exactly once or not called + mockProductRepository.AssertCalled(t, "GetBySku", p.Sku) + mockProductRepository.AssertCalled(t, "Save", p) + + // Assert that the expectations were met + mockProductRepository.AssertExpectations(t) +} diff --git a/internal/user/mock/UserRepository.go b/internal/user/mock/UserRepository.go new file mode 100644 index 0000000..e986424 --- /dev/null +++ b/internal/user/mock/UserRepository.go @@ -0,0 +1,156 @@ +// Code generated by mockery. DO NOT EDIT. + +package mock + +import ( + user "github.com/sebajax/go-vertical-slice-architecture/internal/user" + mock "github.com/stretchr/testify/mock" +) + +// UserRepository is an autogenerated mock type for the UserRepository type +type UserRepository struct { + mock.Mock +} + +type UserRepository_Expecter struct { + mock *mock.Mock +} + +func (_m *UserRepository) EXPECT() *UserRepository_Expecter { + return &UserRepository_Expecter{mock: &_m.Mock} +} + +// GetByEmail provides a mock function with given fields: email +func (_m *UserRepository) GetByEmail(email string) (*user.User, bool, error) { + ret := _m.Called(email) + + if len(ret) == 0 { + panic("no return value specified for GetByEmail") + } + + var r0 *user.User + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func(string) (*user.User, bool, error)); ok { + return rf(email) + } + if rf, ok := ret.Get(0).(func(string) *user.User); ok { + r0 = rf(email) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*user.User) + } + } + + if rf, ok := ret.Get(1).(func(string) bool); ok { + r1 = rf(email) + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(email) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// UserRepository_GetByEmail_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByEmail' +type UserRepository_GetByEmail_Call struct { + *mock.Call +} + +// GetByEmail is a helper method to define mock.On call +// - email string +func (_e *UserRepository_Expecter) GetByEmail(email interface{}) *UserRepository_GetByEmail_Call { + return &UserRepository_GetByEmail_Call{Call: _e.mock.On("GetByEmail", email)} +} + +func (_c *UserRepository_GetByEmail_Call) Run(run func(email string)) *UserRepository_GetByEmail_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *UserRepository_GetByEmail_Call) Return(_a0 *user.User, _a1 bool, _a2 error) *UserRepository_GetByEmail_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *UserRepository_GetByEmail_Call) RunAndReturn(run func(string) (*user.User, bool, error)) *UserRepository_GetByEmail_Call { + _c.Call.Return(run) + return _c +} + +// Save provides a mock function with given fields: u +func (_m *UserRepository) Save(u *user.User) (int64, error) { + ret := _m.Called(u) + + if len(ret) == 0 { + panic("no return value specified for Save") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(*user.User) (int64, error)); ok { + return rf(u) + } + if rf, ok := ret.Get(0).(func(*user.User) int64); ok { + r0 = rf(u) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(*user.User) error); ok { + r1 = rf(u) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UserRepository_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save' +type UserRepository_Save_Call struct { + *mock.Call +} + +// Save is a helper method to define mock.On call +// - u *user.User +func (_e *UserRepository_Expecter) Save(u interface{}) *UserRepository_Save_Call { + return &UserRepository_Save_Call{Call: _e.mock.On("Save", u)} +} + +func (_c *UserRepository_Save_Call) Run(run func(u *user.User)) *UserRepository_Save_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*user.User)) + }) + return _c +} + +func (_c *UserRepository_Save_Call) Return(_a0 int64, _a1 error) *UserRepository_Save_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *UserRepository_Save_Call) RunAndReturn(run func(*user.User) (int64, error)) *UserRepository_Save_Call { + _c.Call.Return(run) + return _c +} + +// NewUserRepository creates a new instance of UserRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUserRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *UserRepository { + mock := &UserRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/user/mock/mockUserRepository.go b/internal/user/mock/mockUserRepository.go deleted file mode 100644 index 8a1ffbc..0000000 --- a/internal/user/mock/mockUserRepository.go +++ /dev/null @@ -1,29 +0,0 @@ -package mocks - -import ( - "time" - - "github.com/sebajax/go-vertical-slice-architecture/internal/user" -) - -type mockUserRepository struct{} - -func NewMockUserRepository() user.UserRepository { - return &mockUserRepository{} -} - -func (mock *mockUserRepository) Save(u *user.User) (int64, error) { - return 1, nil -} - -func (mock *mockUserRepository) GetByEmail(email string) (*user.User, bool, error) { - return &user.User{ - Id: 1, - IdentityNumber: "9999999", - FirstName: "Joe", - LastName: "Doe", - DateOfBirth: time.Now(), - Email: "joe@doe.com", - CreatedAt: time.Now(), - }, true, nil -} diff --git a/internal/user/service/createUser.go b/internal/user/service/createUser.go index 675bfa5..ac37c95 100644 --- a/internal/user/service/createUser.go +++ b/internal/user/service/createUser.go @@ -26,7 +26,7 @@ func (service *CreateUserService) CreateUser(u *user.User) (int64, error) { // check if user does not exist and no database error ocurred if err != nil { // database error - log.Fatalln(err) + log.Println(err) err := apperror.InternalServerError() return 0, err } @@ -41,7 +41,7 @@ func (service *CreateUserService) CreateUser(u *user.User) (int64, error) { id, err := service.userRepository.Save(u) if err != nil { // database error - log.Fatalln(err) + log.Println(err) err := apperror.InternalServerError() return 0, err } diff --git a/internal/user/service/createUser_test.go b/internal/user/service/createUser_test.go new file mode 100644 index 0000000..0dbdfc3 --- /dev/null +++ b/internal/user/service/createUser_test.go @@ -0,0 +1,98 @@ +package service + +import ( + "errors" + "testing" + "time" + + "github.com/sebajax/go-vertical-slice-architecture/internal/user" + "github.com/sebajax/go-vertical-slice-architecture/internal/user/mock" + "github.com/stretchr/testify/assert" +) + +func TestCreateUserService_CreateUser_Success(t *testing.T) { + var insertedId int64 = 1 + // Create a new User + u, _ := user.New("identity_test", "fistname_test", "lastname_test", "email_test", time.Now()) + + // Create a mock UserRepository instance + mockUserRepository := &mock.UserRepository{} + + // Set expectations on the mock + mockUserRepository.EXPECT().GetByEmail(u.Email).Return(nil, false, nil) + mockUserRepository.EXPECT().Save(u).Return(insertedId, nil) + + // Pass the mock as a dependency + userService := NewCreateUserService(mockUserRepository) + + // Call the method being tested + id, err := userService.CreateUser(u) + + // Assert the result + assert.NoError(t, err) + assert.Equal(t, insertedId, id) + + // Assert that the method was called exactly once or not called + mockUserRepository.AssertCalled(t, "GetByEmail", u.Email) + mockUserRepository.AssertCalled(t, "Save", u) + + // Assert that the expectations were met + mockUserRepository.AssertExpectations(t) +} + +func TestCreateProductService_CreateProduct_GetByEmail_Failure(t *testing.T) { + // Create a new User + u, _ := user.New("identity_test", "fistname_test", "lastname_test", "email_test", time.Now()) + + // Create a mock UserRepository instance + mockUserRepository := &mock.UserRepository{} + + // Set expectations on the mock + mockUserRepository.EXPECT().GetByEmail(u.Email).Return(u, true, nil) + + // Pass the mock as a dependency + userService := NewCreateUserService(mockUserRepository) + + // Call the method being tested + id, err := userService.CreateUser(u) + + // Assert the result + assert.Error(t, err) + assert.Equal(t, int64(0), id) + + // Assert that the method was called exactly once or not called + mockUserRepository.AssertCalled(t, "GetByEmail", u.Email) + mockUserRepository.AssertNotCalled(t, "Save") + + // Assert that the expectations were met + mockUserRepository.AssertExpectations(t) +} + +func TestCreateProductService_CreateProduct_Save_Failure(t *testing.T) { + // Create a new User + u, _ := user.New("identity_test", "fistname_test", "lastname_test", "email_test", time.Now()) + + // Create a mock UserRepository instance + mockUserRepository := &mock.UserRepository{} + + // Set expectations on the mock + mockUserRepository.EXPECT().GetByEmail(u.Email).Return(nil, false, nil) + mockUserRepository.EXPECT().Save(u).Return(0, errors.New("DB ERROR")) + + // Pass the mock as a dependency + userService := NewCreateUserService(mockUserRepository) + + // Call the method being tested + id, err := userService.CreateUser(u) + + // Assert the result + assert.Error(t, err) + assert.Equal(t, int64(0), id) + + // Assert that the method was called exactly once or not called + mockUserRepository.AssertCalled(t, "GetByEmail", u.Email) + mockUserRepository.AssertCalled(t, "Save", u) + + // Assert that the expectations were met + mockUserRepository.AssertExpectations(t) +} diff --git a/internal/user/user.go b/internal/user/user.go index 7c77b19..63eb4dd 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -19,7 +19,7 @@ type User struct { } // Create a new user instance -func New(in string, n string, fn string, ln string, e string, dob time.Time) (*User, error) { +func New(in string, fn string, ln string, e string, dob time.Time) (*User, error) { return &User{ IdentityNumber: in, FirstName: fn,