Skip to content

Commit b0d10ef

Browse files
committed
Create book library project with book inserter
0 parents  commit b0d10ef

16 files changed

+669
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea

books/booksinserter/booksinserter.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//go:generate mockery -name=InserterEngine
2+
//go:generate mockery -name=ShelfFinder
3+
4+
package booksinserter
5+
6+
import (
7+
"context"
8+
"errors"
9+
10+
"github.com/rafaelhl/library-rel-lib/books"
11+
)
12+
13+
type (
14+
InserterEngine interface {
15+
InsertBook(ctx context.Context, book books.Book) error
16+
}
17+
18+
ShelfFinder interface {
19+
FindShelf(ctx context.Context, shelfID int) (books.Shelf, error)
20+
}
21+
22+
Inserter struct {
23+
inserterEngine InserterEngine
24+
shelfFinder ShelfFinder
25+
}
26+
)
27+
28+
func New(repository InserterEngine, shelfFinder ShelfFinder) Inserter {
29+
return Inserter{
30+
inserterEngine: repository,
31+
shelfFinder: shelfFinder,
32+
}
33+
}
34+
35+
func (i Inserter) InsertBook(ctx context.Context, book books.Book) error {
36+
shelf, err := i.shelfFinder.FindShelf(ctx, book.BookShelf.ID)
37+
if err != nil {
38+
return err
39+
}
40+
if len(shelf.Books) >= shelf.Capacity {
41+
return errors.New("shelf fully choose another shelf")
42+
}
43+
44+
return i.inserterEngine.InsertBook(ctx, book)
45+
}
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package booksinserter_test
2+
3+
import (
4+
"context"
5+
"errors"
6+
"math/rand"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/rafaelhl/library-rel-lib/books"
12+
"github.com/rafaelhl/library-rel-lib/books/booksinserter"
13+
"github.com/rafaelhl/library-rel-lib/books/booksinserter/mocks"
14+
)
15+
16+
var (
17+
book = books.Book{
18+
Title: "Livro de Teste",
19+
Description: "Esse livro é de teste",
20+
Author: "Rafael Holanda",
21+
Edition: 1,
22+
BookShelf: books.Shelf{
23+
ID: 1,
24+
},
25+
}
26+
shelf = books.Shelf{
27+
ID: 1,
28+
Capacity: 2,
29+
Amount: 0,
30+
}
31+
)
32+
33+
type mocked struct {
34+
inserter *mocks.InserterEngine
35+
finder *mocks.ShelfFinder
36+
}
37+
38+
func (m mocked) assertExpectations(t *testing.T) {
39+
m.inserter.AssertExpectations(t)
40+
m.finder.AssertExpectations(t)
41+
}
42+
43+
func doMock() (booksinserter.Inserter, mocked) {
44+
m := mocked{
45+
inserter: new(mocks.InserterEngine),
46+
finder: new(mocks.ShelfFinder),
47+
}
48+
return booksinserter.New(m.inserter, m.finder), m
49+
}
50+
51+
func TestInserter_InsertBook(t *testing.T) {
52+
ctx := context.Background()
53+
inserter, m := doMock()
54+
m.finder.On("FindShelf", ctx, book.BookShelf.ID).Return(shelf, nil)
55+
m.inserter.On("InsertBook", ctx, book).Return(nil)
56+
57+
err := inserter.InsertBook(ctx, book)
58+
59+
m.assertExpectations(t)
60+
assert.NoError(t, err)
61+
}
62+
63+
func TestInserter_InsertBook_Fail(t *testing.T) {
64+
ctx := context.Background()
65+
inserter, m := doMock()
66+
m.finder.On("FindShelf", ctx, book.BookShelf.ID).Return(shelf, nil)
67+
m.inserter.On("InsertBook", ctx, book).Return(errors.New("unexpected error!"))
68+
69+
err := inserter.InsertBook(ctx, book)
70+
71+
m.assertExpectations(t)
72+
assert.Error(t, err)
73+
}
74+
75+
func TestInserter_InsertBook_FindShelfFail(t *testing.T) {
76+
ctx := context.Background()
77+
inserter, m := doMock()
78+
m.finder.On("FindShelf", ctx, book.BookShelf.ID).Return(books.Shelf{}, errors.New("unexpected error!"))
79+
80+
err := inserter.InsertBook(ctx, book)
81+
82+
m.assertExpectations(t)
83+
assert.Error(t, err)
84+
}
85+
86+
func TestInserter_InsertBook_ShelfFully(t *testing.T) {
87+
ctx := context.Background()
88+
inserter, m := doMock()
89+
s := books.Shelf{
90+
ID: shelf.ID,
91+
Capacity: shelf.Capacity,
92+
Amount: shelf.Amount,
93+
Books: []books.Book{
94+
{
95+
ID: 1,
96+
Title: "Teste 1",
97+
Description: "Teste 1",
98+
Author: "Teste 1",
99+
Edition: rand.Int(),
100+
ShelfID: shelf.ID,
101+
},
102+
{
103+
ID: 2,
104+
Title: "Teste 2",
105+
Description: "Teste 2",
106+
Author: "Teste 2",
107+
Edition: rand.Int(),
108+
ShelfID: shelf.ID,
109+
},
110+
},
111+
}
112+
m.finder.On("FindShelf", ctx, book.BookShelf.ID).Return(s, nil)
113+
114+
err := inserter.InsertBook(ctx, book)
115+
116+
m.assertExpectations(t)
117+
assert.Error(t, err)
118+
}

books/booksinserter/mocks/InserterEngine.go

+30
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

books/booksinserter/mocks/ShelfFinder.go

+37
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

books/entities.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package books
2+
3+
import (
4+
"time"
5+
)
6+
7+
type (
8+
Book struct {
9+
ID int `json:"id"`
10+
Title string `json:"title"`
11+
Description string `json:"description"`
12+
Author string `json:"author"`
13+
Edition int `json:"edition"`
14+
ShelfID int `db:"shelf_id"`
15+
CreatedAt time.Time `json:"-"`
16+
UpdatedAt time.Time `json:"-"`
17+
BookShelf Shelf `json:"shelf" ref:"shelf_id" fk:"id"`
18+
}
19+
20+
Shelf struct {
21+
ID int `json:"id"`
22+
Capacity int `json:"-"`
23+
Amount int `json:"-"`
24+
Books []Book `json:"-" ref:"id" fk:"shelf_id"`
25+
}
26+
)
27+
28+
func (Book) Table() string {
29+
return "book"
30+
}
31+
32+
func (Shelf) Table() string {
33+
return "shelf"
34+
}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//go:generate mockery -name=BookInserter
2+
3+
package bookinsert
4+
5+
import (
6+
"context"
7+
"encoding/json"
8+
"net/http"
9+
10+
"github.com/rafaelhl/library-rel-lib/books"
11+
)
12+
13+
type (
14+
BookInserter interface {
15+
InsertBook(ctx context.Context, book books.Book) error
16+
}
17+
18+
handler struct {
19+
inserter BookInserter
20+
}
21+
)
22+
23+
func NewHandler(inserter BookInserter) handler {
24+
return handler{
25+
inserter: inserter,
26+
}
27+
}
28+
29+
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
30+
var book books.Book
31+
err := json.NewDecoder(r.Body).Decode(&book)
32+
if err != nil {
33+
w.WriteHeader(http.StatusInternalServerError)
34+
return
35+
}
36+
37+
err = h.inserter.InsertBook(r.Context(), book)
38+
if err != nil {
39+
w.WriteHeader(http.StatusInternalServerError)
40+
return
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package bookinsert_test
2+
3+
import (
4+
"errors"
5+
"io"
6+
"io/ioutil"
7+
"net/http"
8+
"net/http/httptest"
9+
"strings"
10+
"testing"
11+
12+
"github.com/go-chi/chi"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/mock"
15+
16+
"github.com/rafaelhl/library-rel-lib/books"
17+
"github.com/rafaelhl/library-rel-lib/books/handler/bookinsert"
18+
"github.com/rafaelhl/library-rel-lib/books/handler/bookinsert/mocks"
19+
)
20+
21+
var (
22+
body, _ = ioutil.ReadFile("mocks/book.json")
23+
book = books.Book{
24+
Title: "Livro de Teste",
25+
Description: "Esse livro é de teste",
26+
Author: "Rafael Holanda",
27+
Edition: 1,
28+
BookShelf: books.Shelf{
29+
ID: 1,
30+
},
31+
}
32+
)
33+
34+
func TestHandler_ServeHTTP(t *testing.T) {
35+
inserter := new(mocks.BookInserter)
36+
inserter.On("InsertBook", mock.Anything, book).Return(nil)
37+
38+
recorder := sendRequest(inserter, strings.NewReader(string(body)))
39+
40+
inserter.AssertExpectations(t)
41+
42+
assert.Equal(t, http.StatusOK, recorder.Code)
43+
inserter.AssertExpectations(t)
44+
}
45+
46+
func TestHandler_ServeHTTP_InvalidPayload(t *testing.T) {
47+
inserter := new(mocks.BookInserter)
48+
recorder := sendRequest(inserter, strings.NewReader("{"))
49+
50+
inserter.AssertExpectations(t)
51+
52+
assert.Equal(t, http.StatusInternalServerError, recorder.Code)
53+
inserter.AssertExpectations(t)
54+
}
55+
56+
func TestHandler_ServeHTTP_InsertFail(t *testing.T) {
57+
inserter := new(mocks.BookInserter)
58+
inserter.On("InsertBook", mock.Anything, book).Return(errors.New("unexpected error!"))
59+
60+
recorder := sendRequest(inserter, strings.NewReader(string(body)))
61+
62+
inserter.AssertExpectations(t)
63+
64+
assert.Equal(t, http.StatusInternalServerError, recorder.Code)
65+
inserter.AssertExpectations(t)
66+
}
67+
68+
func sendRequest(inserter *mocks.BookInserter, body io.Reader) *httptest.ResponseRecorder {
69+
mux := chi.NewMux()
70+
handler := bookinsert.NewHandler(inserter)
71+
mux.Method(http.MethodPost, "/books", handler)
72+
73+
recorder := httptest.NewRecorder()
74+
mux.ServeHTTP(recorder, httptest.NewRequest(http.MethodPost, "/books", body))
75+
return recorder
76+
}

0 commit comments

Comments
 (0)