diff --git a/README.md b/README.md index 00e150d..140f765 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Clean Architecture sample ## API requests -### Add a book +### Add book ``` curl -X "POST" "http://localhost:8080/v1/book" \ @@ -32,7 +32,7 @@ curl -X "POST" "http://localhost:8080/v1/book" \ "quantity":1 }' ``` -### Search a book +### Search book ``` curl "http://localhost:8080/v1/book?title=ozzy" \ @@ -40,7 +40,7 @@ curl "http://localhost:8080/v1/book?title=ozzy" \ -H 'Accept: application/json' ``` -### Show all books +### Show books ``` curl "http://localhost:8080/v1/book" \ @@ -48,6 +48,37 @@ curl "http://localhost:8080/v1/book" \ -H 'Accept: application/json' ``` +### Add user + +``` +curl -X "POST" "http://localhost:8080/v1/user" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d $'{ + "email": "ozzy@metal.net", + "fist_name": "Ozzy", + "last_name": "Osbourne", + "password": "bateater666", + "quantity":1 +}' + +``` +### Search user + +``` +curl "http://localhost:8080/v1/user?name=ozzy" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' +``` + +### Show users + +``` +curl "http://localhost:8080/v1/user" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' +``` + ## CMD ### Search for a book diff --git a/api/handler/loan.go b/api/handler/loan.go index 65a5e9a..27fe621 100644 --- a/api/handler/loan.go +++ b/api/handler/loan.go @@ -16,7 +16,7 @@ import ( "github.com/gorilla/mux" ) -func borrow(bService book.UseCase, uService user.UseCase, loanService loan.UseCase) http.Handler { +func borrowBook(bService book.UseCase, uService user.UseCase, loanService loan.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error borrowing book" vars := mux.Vars(r) @@ -32,21 +32,60 @@ func borrow(bService book.UseCase, uService user.UseCase, loanService loan.UseCa w.Write([]byte(errorMessage)) return } + if b == nil { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(errorMessage)) + return + } uID, err := entity.StringToID(vars["user_id"]) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return } - u, err := uService.Get(uID) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return } - + if u == nil { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(errorMessage)) + return + } err = loanService.Borrow(u, b) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(errorMessage)) + return + } + w.WriteHeader(http.StatusCreated) + }) +} + +func returnBook(bService book.UseCase, loanService loan.UseCase) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + errorMessage := "Error returning book" + vars := mux.Vars(r) + bID, err := entity.StringToID(vars["book_id"]) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(errorMessage)) + return + } + b, err := bService.Get(bID) + if err != nil && err != domain.ErrNotFound { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(errorMessage)) + return + } + if b == nil { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(errorMessage)) + return + } + err = loanService.Return(b) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -59,10 +98,10 @@ func borrow(bService book.UseCase, uService user.UseCase, loanService loan.UseCa //MakeLoanHandlers make url handlers func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bService book.UseCase, uService user.UseCase, loanService loan.UseCase) { r.Handle("/v1/loan/borrow/{book_id}/{user_id}", n.With( - negroni.Wrap(borrow(bService, uService, loanService)), - )).Methods("GET", "OPTIONS").Name("borrow") + negroni.Wrap(borrowBook(bService, uService, loanService)), + )).Methods("GET", "OPTIONS").Name("borrowBook") - //r.Handle("/v1/book/{book_id}/return", n.With( - // negroni.Wrap(returnBook(service)), - //)).Methods("GET", "OPTIONS").Name("returnBook") + r.Handle("/v1/loan/return/{book_id}", n.With( + negroni.Wrap(returnBook(bService, loanService)), + )).Methods("GET", "OPTIONS").Name("returnBook") } diff --git a/api/handler/loan_test.go b/api/handler/loan_test.go new file mode 100644 index 0000000..6c0ef80 --- /dev/null +++ b/api/handler/loan_test.go @@ -0,0 +1,107 @@ +package handler + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" + + "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" + + "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" + + "github.com/codegangsta/negroni" + bmock "github.com/eminetto/clean-architecture-go-v2/domain/entity/book/mock" + umock "github.com/eminetto/clean-architecture-go-v2/domain/entity/user/mock" + lmock "github.com/eminetto/clean-architecture-go-v2/domain/loan/mock" + "github.com/golang/mock/gomock" + "github.com/gorilla/mux" + "github.com/stretchr/testify/assert" +) + +func Test_borrowBook(t *testing.T) { + controller := gomock.NewController(t) + defer controller.Finish() + uMock := umock.NewMockUseCase(controller) + bMock := bmock.NewMockUseCase(controller) + lMock := lmock.NewMockUseCase(controller) + r := mux.NewRouter() + n := negroni.New() + MakeLoanHandlers(r, *n, bMock, uMock, lMock) + path, err := r.GetRoute("borrowBook").GetPathTemplate() + assert.Nil(t, err) + assert.Equal(t, "/v1/loan/borrow/{book_id}/{user_id}", path) + handler := borrowBook(bMock, uMock, lMock) + r.Handle("/v1/loan/borrow/{book_id}/{user_id}", handler) + t.Run("book not found", func(t *testing.T) { + bID := entity.NewID() + uID := entity.NewID() + bMock.EXPECT().Get(bID).Return(nil, domain.ErrNotFound) + ts := httptest.NewServer(r) + defer ts.Close() + res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, bID.String(), uID.String())) + assert.Nil(t, err) + assert.Equal(t, http.StatusNotFound, res.StatusCode) + }) + t.Run("user not found", func(t *testing.T) { + b := book.NewFixtureBook() + uID := entity.NewID() + bMock.EXPECT().Get(b.ID).Return(b, nil) + uMock.EXPECT().Get(uID).Return(nil, domain.ErrNotFound) + ts := httptest.NewServer(r) + defer ts.Close() + res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, b.ID.String(), uID.String())) + assert.Nil(t, err) + assert.Equal(t, http.StatusNotFound, res.StatusCode) + }) + t.Run("success", func(t *testing.T) { + b := book.NewFixtureBook() + u := user.NewFixtureUser() + bMock.EXPECT().Get(b.ID).Return(b, nil) + uMock.EXPECT().Get(u.ID).Return(u, nil) + lMock.EXPECT().Borrow(u, b).Return(nil) + ts := httptest.NewServer(r) + defer ts.Close() + res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, b.ID.String(), u.ID.String())) + assert.Nil(t, err) + assert.Equal(t, http.StatusCreated, res.StatusCode) + }) +} + +func Test_returnBook(t *testing.T) { + controller := gomock.NewController(t) + defer controller.Finish() + uMock := umock.NewMockUseCase(controller) + bMock := bmock.NewMockUseCase(controller) + lMock := lmock.NewMockUseCase(controller) + r := mux.NewRouter() + n := negroni.New() + MakeLoanHandlers(r, *n, bMock, uMock, lMock) + path, err := r.GetRoute("returnBook").GetPathTemplate() + assert.Nil(t, err) + assert.Equal(t, "/v1/loan/return/{book_id}", path) + handler := returnBook(bMock, lMock) + r.Handle("/v1/loan/return/{book_id}", handler) + t.Run("book not found", func(t *testing.T) { + bID := entity.NewID() + bMock.EXPECT().Get(bID).Return(nil, domain.ErrNotFound) + ts := httptest.NewServer(r) + defer ts.Close() + res, err := http.Get(fmt.Sprintf("%s/v1/loan/return/%s", ts.URL, bID.String())) + assert.Nil(t, err) + assert.Equal(t, http.StatusNotFound, res.StatusCode) + }) + t.Run("success", func(t *testing.T) { + b := book.NewFixtureBook() + bMock.EXPECT().Get(b.ID).Return(b, nil) + lMock.EXPECT().Return(b).Return(nil) + ts := httptest.NewServer(r) + defer ts.Close() + res, err := http.Get(fmt.Sprintf("%s/v1/loan/return/%s", ts.URL, b.ID.String())) + assert.Nil(t, err) + assert.Equal(t, http.StatusCreated, res.StatusCode) + }) +}