package api import ( "bytes" "database/sql" "encoding/json" "fmt" "io/ioutil" "net/http" "net/http/httptest" "testing" "time" mockdb "git.nochill.in/nochill/naice_pos/db/mock" db "git.nochill.in/nochill/naice_pos/db/sqlc" "git.nochill.in/nochill/naice_pos/token" "git.nochill.in/nochill/naice_pos/util" "github.com/gin-gonic/gin" "github.com/golang/mock/gomock" "github.com/google/uuid" "github.com/stretchr/testify/require" ) var MERCHANTID = "7e525a4b-4208-4f05-99a1-a75df475dd9b" func TestGetProductApi(t *testing.T) { product := randomProduct(MERCHANTID) testCases := []struct { name string productID string buildStubs func(store *mockdb.MockStore) setupAuth func(t *testing.T, request *http.Request, tokenMaker token.Maker) checkResponse func(t *testing.T, recorder *httptest.ResponseRecorder) }{ { name: "OK", productID: product.ID.String(), buildStubs: func(store *mockdb.MockStore) { store.EXPECT(). GetProduct(gomock.Any(), gomock.Eq(product.ID)). Times(1). Return(product, nil) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusOK, recorder.Code) }, }, { name: "Not found", productID: product.ID.String(), buildStubs: func(store *mockdb.MockStore) { store.EXPECT(). GetProduct(gomock.Any(), gomock.Eq(product.ID)). Times(1). Return(db.Product{}, sql.ErrNoRows) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusNotFound, recorder.Code) }, }, { name: "Unauthorized", productID: product.ID.String(), buildStubs: func(store *mockdb.MockStore) { store.EXPECT(). GetProduct(gomock.Any(), gomock.Eq(product.ID)). Times(1). Return(db.Product{}, sql.ErrNoRows) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", uuid.New().String(), time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusNotFound, recorder.Code) }, }, { name: "NoAuthorization", productID: product.ID.String(), buildStubs: func(store *mockdb.MockStore) { store.EXPECT(). GetProduct(gomock.Any(), gomock.Any()). Times(0) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusUnauthorized, recorder.Code) }, }, { name: "Internal Error", productID: product.ID.String(), buildStubs: func(store *mockdb.MockStore) { store.EXPECT(). GetProduct(gomock.Any(), gomock.Eq(product.ID)). Times(1). Return(db.Product{}, sql.ErrConnDone) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusInternalServerError, recorder.Code) }, }, { name: "Bad request", productID: "0", buildStubs: func(store *mockdb.MockStore) { store.EXPECT(). GetProduct(gomock.Any(), gomock.Any()). Times(0) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusBadRequest, recorder.Code) }, }, } for i := range testCases { tc := testCases[i] t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() store := mockdb.NewMockStore(ctrl) tc.buildStubs(store) server := newTestServer(t, store) recorder := httptest.NewRecorder() url := fmt.Sprintf("/api/product/%s", tc.productID) request, err := http.NewRequest(http.MethodGet, url, nil) require.NoError(t, err) tc.setupAuth(t, request, server.tokenMaker) server.router.ServeHTTP(recorder, request) tc.checkResponse(t, recorder) }) } } func TestCreateProductApi(t *testing.T) { product := randomProduct(MERCHANTID) testCases := []struct { name string body gin.H buildStubs func(store *mockdb.MockStore) setupAuth func(t *testing.T, request *http.Request, tokenMaker token.Maker) checkResponse func(t *testing.T, recorder *httptest.ResponseRecorder) }{ { name: "OK", body: gin.H{ "name": product.Name, "selling_price": product.SellingPrice, "purchase_price": product.PurchasePrice, "product_type_id": product.ProductTypeID, "product_category_id": product.ProductCategoryID, "stock": product.Stock, }, buildStubs: func(store *mockdb.MockStore) { arg := db.CreateProductParams{ MerchantID: product.MerchantID, Name: product.Name, ProductTypeID: product.ProductTypeID, SellingPrice: product.SellingPrice, PurchasePrice: product.PurchasePrice, ProductCategoryID: product.ProductCategoryID, Stock: product.Stock, } store.EXPECT(). CreateProduct(gomock.Any(), gomock.Eq(arg)). Times(1). Return(product, nil) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusOK, recorder.Code) }, }, } for i := range testCases { tc := testCases[i] t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() store := mockdb.NewMockStore(ctrl) tc.buildStubs(store) data, err := json.Marshal(tc.body) require.NoError(t, err) server := newTestServer(t, store) recorder := httptest.NewRecorder() url := "/api/products" request, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data)) require.NoError(t, err) tc.setupAuth(t, request, server.tokenMaker) server.router.ServeHTTP(recorder, request) tc.checkResponse(t, recorder) }) } } func randomProduct(merchantID string) db.Product { productCategory := createRandomProductCategory() return db.Product{ ID: uuid.New(), MerchantID: uuid.MustParse(MERCHANTID), ProductTypeID: 1, Name: util.RandomString(5), SellingPrice: util.RandomFloat(1000, 99999), PurchasePrice: util.RandomFloat(999, 9999), ProductCategoryID: productCategory.ID, Stock: util.RandomFloat(100, 99999), } } func requireBodyMatchAccount(t *testing.T, body *bytes.Buffer, product db.Product) { data, err := ioutil.ReadAll(body) require.NoError(t, err) var gotProduct db.Product err = json.Unmarshal(data, &gotProduct) require.NoError(t, err) require.Equal(t, product, gotProduct) }