diff --git a/api/product.go b/api/product.go index 66d55a5..d7d8416 100644 --- a/api/product.go +++ b/api/product.go @@ -13,11 +13,11 @@ import ( ) type createProductRequest struct { - MerchantID uuid.UUID `json:"merchant_id" binding:"required"` - Name string `json:"name" binding:"required"` - SellingPrice float64 `json:"selling_price" binding:"required"` - PurchasePrice float64 `json:"purchase_price" binding:"required"` - Stock float64 `json:"stock" binding:"number"` + Name string `json:"name" binding:"required"` + SellingPrice float64 `json:"selling_price" binding:"required"` + PurchasePrice float64 `json:"purchase_price" binding:"required"` + ProductCategoryID string `json:"product_category_id" binding:"required,uuid"` + Stock float64 `json:"stock" binding:"number"` } func (server *Server) createProduct(ctx *gin.Context) { @@ -30,11 +30,12 @@ func (server *Server) createProduct(ctx *gin.Context) { authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) arg := db.CreateProductParams{ - MerchantID: authPayload.MerchantID, - Name: req.Name, - SellingPrice: req.SellingPrice, - PurchasePrice: req.PurchasePrice, - Stock: req.Stock, + MerchantID: authPayload.MerchantID, + Name: req.Name, + SellingPrice: req.SellingPrice, + PurchasePrice: req.PurchasePrice, + ProductCategoryID: uuid.MustParse(req.ProductCategoryID), + Stock: req.Stock, } product, err := server.store.CreateProduct(ctx, arg) diff --git a/api/product_category_test.go b/api/product_category_test.go index 69dd671..d666d4c 100644 --- a/api/product_category_test.go +++ b/api/product_category_test.go @@ -19,7 +19,7 @@ import ( ) func TestCreateProductCategory(t *testing.T) { - productCategory := createRandomProductCategory(t) + productCategory := createRandomProductCategory() testCases := []struct { name string @@ -80,7 +80,7 @@ func TestCreateProductCategory(t *testing.T) { } } -func createRandomProductCategory(t *testing.T) db.ProductCategory { +func createRandomProductCategory() db.ProductCategory { return db.ProductCategory{ ID: uuid.New(), MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), diff --git a/api/product_test.go b/api/product_test.go index 274e00c..3a7fdee 100644 --- a/api/product_test.go +++ b/api/product_test.go @@ -15,14 +15,16 @@ import ( 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 = "a848090f-0409-4386-9caa-929ae6874dbb" + func TestGetProductApi(t *testing.T) { - merchantID := "f9ca13cf-8ab3-4ee3-9530-521ae505caa2" - product := randomProduct(merchantID) + product := randomProduct(MERCHANTID) testCases := []struct { name string @@ -41,11 +43,10 @@ func TestGetProductApi(t *testing.T) { Return(product, nil) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { - addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", merchantID, time.Minute) + addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusOK, recorder.Code) - requireBodyMatchAccount(t, recorder.Body, product) }, }, { @@ -58,7 +59,7 @@ func TestGetProductApi(t *testing.T) { 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) + addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusNotFound, recorder.Code) @@ -104,7 +105,7 @@ func TestGetProductApi(t *testing.T) { 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) + addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusInternalServerError, recorder.Code) @@ -119,7 +120,7 @@ func TestGetProductApi(t *testing.T) { Times(0) }, setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { - addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", merchantID, time.Minute) + addAuthorization(t, request, tokenMaker, authorizationTypeBearer, "email", MERCHANTID, time.Minute) }, checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) { require.Equal(t, http.StatusBadRequest, recorder.Code) @@ -153,14 +154,87 @@ func TestGetProductApi(t *testing.T) { } } +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_category_id": product.ProductCategoryID, + "stock": product.Stock, + }, + buildStubs: func(store *mockdb.MockStore) { + arg := db.CreateProductParams{ + MerchantID: product.MerchantID, + Name: product.Name, + 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("f9ca13cf-8ab3-4ee3-9530-521ae505caa2"), - Name: util.RandomString(5), - SellingPrice: util.RandomFloat(1000, 99999), - PurchasePrice: util.RandomFloat(999, 9999), - Stock: util.RandomFloat(100, 99999), + ID: uuid.New(), + MerchantID: uuid.MustParse(MERCHANTID), + Name: util.RandomString(5), + SellingPrice: util.RandomFloat(1000, 99999), + PurchasePrice: util.RandomFloat(999, 9999), + ProductCategoryID: productCategory.ID, + Stock: util.RandomFloat(100, 99999), } } diff --git a/db/migrations/000004_alter_products_category_id.down.sql b/db/migrations/000004_alter_products_category_id.down.sql new file mode 100644 index 0000000..7b62890 --- /dev/null +++ b/db/migrations/000004_alter_products_category_id.down.sql @@ -0,0 +1 @@ +ALTER TABLE "products" ALTER COLUMN "product_category_id" SET DEFAULT gen_random_uuid(); \ No newline at end of file diff --git a/db/migrations/000004_alter_products_category_id.up.sql b/db/migrations/000004_alter_products_category_id.up.sql new file mode 100644 index 0000000..cca5b81 --- /dev/null +++ b/db/migrations/000004_alter_products_category_id.up.sql @@ -0,0 +1 @@ +ALTER TABLE products ALTER COLUMN product_category_id DROP DEFAULT; \ No newline at end of file diff --git a/db/query/products.sql b/db/query/product.sql similarity index 81% rename from db/query/products.sql rename to db/query/product.sql index 11b0f01..2299582 100644 --- a/db/query/products.sql +++ b/db/query/product.sql @@ -4,9 +4,10 @@ INSERT INTO products ( name, selling_price, purchase_price, + product_category_id, stock ) VALUES ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4, $5, $6 ) RETURNING *; @@ -34,7 +35,7 @@ OFFSET $3; -- name: UpdateProduct :one UPDATE products -SET name = $2, selling_price = $3, purchase_price = $4, updated_at = $5 +SET name = $2, selling_price = $3, purchase_price = $4, product_category_id = $5, updated_at = $6 WHERE id = $1 RETURNING *; diff --git a/db/sqlc/products.sql.go b/db/sqlc/product.sql.go similarity index 84% rename from db/sqlc/products.sql.go rename to db/sqlc/product.sql.go index 01c1a3d..95ec5f2 100644 --- a/db/sqlc/products.sql.go +++ b/db/sqlc/product.sql.go @@ -1,7 +1,7 @@ // Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.17.2 -// source: products.sql +// source: product.sql package db @@ -18,19 +18,21 @@ INSERT INTO products ( name, selling_price, purchase_price, + product_category_id, stock ) VALUES ( - $1, $2, $3, $4, $5 + $1, $2, $3, $4, $5, $6 ) RETURNING id, merchant_id, index_id, name, selling_price, purchase_price, stock, created_at, updated_at, product_category_id ` type CreateProductParams struct { - MerchantID uuid.UUID `json:"merchant_id"` - Name string `json:"name"` - SellingPrice float64 `json:"selling_price"` - PurchasePrice float64 `json:"purchase_price"` - Stock float64 `json:"stock"` + MerchantID uuid.UUID `json:"merchant_id"` + Name string `json:"name"` + SellingPrice float64 `json:"selling_price"` + PurchasePrice float64 `json:"purchase_price"` + ProductCategoryID uuid.UUID `json:"product_category_id"` + Stock float64 `json:"stock"` } func (q *Queries) CreateProduct(ctx context.Context, arg CreateProductParams) (Product, error) { @@ -39,6 +41,7 @@ func (q *Queries) CreateProduct(ctx context.Context, arg CreateProductParams) (P arg.Name, arg.SellingPrice, arg.PurchasePrice, + arg.ProductCategoryID, arg.Stock, ) var i Product @@ -164,17 +167,18 @@ func (q *Queries) ListProducts(ctx context.Context, arg ListProductsParams) ([]P const updateProduct = `-- name: UpdateProduct :one UPDATE products -SET name = $2, selling_price = $3, purchase_price = $4, updated_at = $5 +SET name = $2, selling_price = $3, purchase_price = $4, product_category_id = $5, updated_at = $6 WHERE id = $1 RETURNING id, merchant_id, index_id, name, selling_price, purchase_price, stock, created_at, updated_at, product_category_id ` type UpdateProductParams struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - SellingPrice float64 `json:"selling_price"` - PurchasePrice float64 `json:"purchase_price"` - UpdatedAt sql.NullTime `json:"updated_at"` + ID uuid.UUID `json:"id"` + Name string `json:"name"` + SellingPrice float64 `json:"selling_price"` + PurchasePrice float64 `json:"purchase_price"` + ProductCategoryID uuid.UUID `json:"product_category_id"` + UpdatedAt sql.NullTime `json:"updated_at"` } func (q *Queries) UpdateProduct(ctx context.Context, arg UpdateProductParams) (Product, error) { @@ -183,6 +187,7 @@ func (q *Queries) UpdateProduct(ctx context.Context, arg UpdateProductParams) (P arg.Name, arg.SellingPrice, arg.PurchasePrice, + arg.ProductCategoryID, arg.UpdatedAt, ) var i Product diff --git a/db/sqlc/products_test.go b/db/sqlc/product_test.go similarity index 92% rename from db/sqlc/products_test.go rename to db/sqlc/product_test.go index 6098649..a590e28 100644 --- a/db/sqlc/products_test.go +++ b/db/sqlc/product_test.go @@ -16,13 +16,15 @@ func createRandomProduct(t *testing.T) (Product, CreateProductParams) { sellingPrice := util.RandomFloat(999, 99999) purchasePrice := util.RandomFloat(999, 9999) stock := util.RandomFloat(10, 10000) + productCategory := createRandomProductCategory(t) arg := CreateProductParams{ - MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), - Name: util.RandomString(10), - SellingPrice: sellingPrice, - PurchasePrice: purchasePrice, - Stock: stock, + MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), + Name: util.RandomString(10), + SellingPrice: sellingPrice, + PurchasePrice: purchasePrice, + ProductCategoryID: productCategory.ID, + Stock: stock, } product, err := testQueries.CreateProduct(context.Background(), arg)