mock test

This commit is contained in:
nochill 2023-03-12 11:01:43 +07:00
parent a2c799cb29
commit 85dd2a3134
19 changed files with 640 additions and 34 deletions

View File

@ -6,6 +6,10 @@ migrateup:
migratedown:
migrate -path db/migrations -database "${DB_TYPE}://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable" -verbose down
mock-generate:
mockgen -package mockdb -destination db/mock/store.go git.nochill.in/nochill/naice_pos/db/sqlc Store
sqlc:
sqlc generate

15
api/main_test.go Normal file
View File

@ -0,0 +1,15 @@
package api
import (
"os"
"testing"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)
func TestMain(m *testing.M) {
gin.SetMode(gin.TestMode)
os.Exit(m.Run())
}

View File

@ -1,7 +1,9 @@
package api
import (
"database/sql"
"net/http"
"time"
db "git.nochill.in/nochill/naice_pos/db/sqlc"
"github.com/gin-gonic/gin"
@ -13,7 +15,7 @@ type createProductRequest struct {
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:"required"`
Stock float64 `json:"stock" binding:"number"`
}
func (server *Server) createProduct(ctx *gin.Context) {
@ -40,3 +42,62 @@ func (server *Server) createProduct(ctx *gin.Context) {
ctx.JSON(http.StatusOK, product)
}
type getProductRequest struct {
ID string `uri:"id" binding:"required,uuid"`
}
func (server *Server) getProduct(ctx *gin.Context) {
var req getProductRequest
if err := ctx.ShouldBindUri(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}
product, err := server.store.GetProduct(ctx, uuid.MustParse(req.ID))
if err != nil {
if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return
}
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
return
}
ctx.JSON(http.StatusOK, product)
}
type updateProductRequest struct {
ProductID uuid.UUID `json:"product_id" binding:"required"`
Name string `json:"name" binding:"required"`
SellingPrice float64 `json:"selling_price" binding:"required"`
PurchasePrice float64 `json:"purchase_price" binding:"required"`
}
func (server *Server) updateProduct(ctx *gin.Context) {
var req updateProductRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}
arg := db.UpdateProductParams{
ID: req.ProductID,
Name: req.Name,
SellingPrice: req.SellingPrice,
PurchasePrice: req.PurchasePrice,
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
}
product, err := server.store.UpdateProduct(ctx, arg)
if err != nil {
if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return
}
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
return
}
ctx.JSON(http.StatusOK, product)
}

128
api/product_test.go Normal file
View File

@ -0,0 +1,128 @@
package api
import (
"bytes"
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
mockdb "git.nochill.in/nochill/naice_pos/db/mock"
db "git.nochill.in/nochill/naice_pos/db/sqlc"
"git.nochill.in/nochill/naice_pos/util"
"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func TestGetProductApi(t *testing.T) {
product := randomProduct()
testCases := []struct {
name string
productID string
buildStubs func(store *mockdb.MockStore)
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)
},
checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) {
require.Equal(t, http.StatusOK, recorder.Code)
requireBodyMatchAccount(t, recorder.Body, product)
},
},
{
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)
},
checkResponse: func(t *testing.T, recorder *httptest.ResponseRecorder) {
require.Equal(t, http.StatusNotFound, 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)
},
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)
},
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 := NewServer(store)
recorder := httptest.NewRecorder()
url := fmt.Sprintf("/product/%s", tc.productID)
request, err := http.NewRequest(http.MethodGet, url, nil)
require.NoError(t, err)
server.router.ServeHTTP(recorder, request)
tc.checkResponse(t, recorder)
})
}
}
func randomProduct() db.Product {
return db.Product{
ID: uuid.New(),
MerchantID: uuid.MustParse("d9b0e126-991e-46ac-8c61-5efdd605f75d"),
Name: util.RandomString(5),
SellingPrice: util.RandomFloat(1000, 99999),
PurchasePrice: util.RandomFloat(999, 9999),
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)
}

View File

@ -6,15 +6,17 @@ import (
)
type Server struct {
store *db.Store
store db.Store
router *gin.Engine
}
func NewServer(store *db.Store) *Server {
func NewServer(store db.Store) *Server {
server := &Server{store: store}
router := gin.Default()
router.POST("/products", server.createProduct)
router.PATCH("/products", server.updateProduct)
router.GET("/product/:id", server.getProduct)
router.POST("/suppliers", server.createSupplier)
router.POST("/purchase-products", server.createPurchase)

333
db/mock/store.go Normal file
View File

@ -0,0 +1,333 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: git.nochill.in/nochill/naice_pos/db/sqlc (interfaces: Store)
// Package mockdb is a generated GoMock package.
package mockdb
import (
context "context"
reflect "reflect"
db "git.nochill.in/nochill/naice_pos/db/sqlc"
gomock "github.com/golang/mock/gomock"
uuid "github.com/google/uuid"
)
// MockStore is a mock of Store interface.
type MockStore struct {
ctrl *gomock.Controller
recorder *MockStoreMockRecorder
}
// MockStoreMockRecorder is the mock recorder for MockStore.
type MockStoreMockRecorder struct {
mock *MockStore
}
// NewMockStore creates a new mock instance.
func NewMockStore(ctrl *gomock.Controller) *MockStore {
mock := &MockStore{ctrl: ctrl}
mock.recorder = &MockStoreMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
return m.recorder
}
// CreateCustomers mocks base method.
func (m *MockStore) CreateCustomers(arg0 context.Context, arg1 db.CreateCustomersParams) (db.Customer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateCustomers", arg0, arg1)
ret0, _ := ret[0].(db.Customer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateCustomers indicates an expected call of CreateCustomers.
func (mr *MockStoreMockRecorder) CreateCustomers(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCustomers", reflect.TypeOf((*MockStore)(nil).CreateCustomers), arg0, arg1)
}
// CreateProduct mocks base method.
func (m *MockStore) CreateProduct(arg0 context.Context, arg1 db.CreateProductParams) (db.Product, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateProduct", arg0, arg1)
ret0, _ := ret[0].(db.Product)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateProduct indicates an expected call of CreateProduct.
func (mr *MockStoreMockRecorder) CreateProduct(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateProduct", reflect.TypeOf((*MockStore)(nil).CreateProduct), arg0, arg1)
}
// CreatePurchaseOrder mocks base method.
func (m *MockStore) CreatePurchaseOrder(arg0 context.Context, arg1 db.CreatePurchaseOrderParams) (db.PurchaseOrder, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreatePurchaseOrder", arg0, arg1)
ret0, _ := ret[0].(db.PurchaseOrder)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreatePurchaseOrder indicates an expected call of CreatePurchaseOrder.
func (mr *MockStoreMockRecorder) CreatePurchaseOrder(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePurchaseOrder", reflect.TypeOf((*MockStore)(nil).CreatePurchaseOrder), arg0, arg1)
}
// CreatePurchaseOrderDetail mocks base method.
func (m *MockStore) CreatePurchaseOrderDetail(arg0 context.Context, arg1 db.CreatePurchaseOrderDetailParams) (db.PurchaseOrderDetail, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreatePurchaseOrderDetail", arg0, arg1)
ret0, _ := ret[0].(db.PurchaseOrderDetail)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreatePurchaseOrderDetail indicates an expected call of CreatePurchaseOrderDetail.
func (mr *MockStoreMockRecorder) CreatePurchaseOrderDetail(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePurchaseOrderDetail", reflect.TypeOf((*MockStore)(nil).CreatePurchaseOrderDetail), arg0, arg1)
}
// CreateSuppliers mocks base method.
func (m *MockStore) CreateSuppliers(arg0 context.Context, arg1 db.CreateSuppliersParams) (db.Supplier, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateSuppliers", arg0, arg1)
ret0, _ := ret[0].(db.Supplier)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateSuppliers indicates an expected call of CreateSuppliers.
func (mr *MockStoreMockRecorder) CreateSuppliers(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSuppliers", reflect.TypeOf((*MockStore)(nil).CreateSuppliers), arg0, arg1)
}
// CustomersList mocks base method.
func (m *MockStore) CustomersList(arg0 context.Context, arg1 db.CustomersListParams) ([]db.Customer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CustomersList", arg0, arg1)
ret0, _ := ret[0].([]db.Customer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CustomersList indicates an expected call of CustomersList.
func (mr *MockStoreMockRecorder) CustomersList(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomersList", reflect.TypeOf((*MockStore)(nil).CustomersList), arg0, arg1)
}
// DeleteCustomer mocks base method.
func (m *MockStore) DeleteCustomer(arg0 context.Context, arg1 uuid.UUID) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteCustomer", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteCustomer indicates an expected call of DeleteCustomer.
func (mr *MockStoreMockRecorder) DeleteCustomer(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCustomer", reflect.TypeOf((*MockStore)(nil).DeleteCustomer), arg0, arg1)
}
// DeleteProduct mocks base method.
func (m *MockStore) DeleteProduct(arg0 context.Context, arg1 uuid.UUID) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteProduct", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteProduct indicates an expected call of DeleteProduct.
func (mr *MockStoreMockRecorder) DeleteProduct(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteProduct", reflect.TypeOf((*MockStore)(nil).DeleteProduct), arg0, arg1)
}
// DeleteSupplier mocks base method.
func (m *MockStore) DeleteSupplier(arg0 context.Context, arg1 uuid.UUID) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteSupplier", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteSupplier indicates an expected call of DeleteSupplier.
func (mr *MockStoreMockRecorder) DeleteSupplier(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSupplier", reflect.TypeOf((*MockStore)(nil).DeleteSupplier), arg0, arg1)
}
// GetMerchantById mocks base method.
func (m *MockStore) GetMerchantById(arg0 context.Context, arg1 uuid.UUID) (db.Merchant, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetMerchantById", arg0, arg1)
ret0, _ := ret[0].(db.Merchant)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetMerchantById indicates an expected call of GetMerchantById.
func (mr *MockStoreMockRecorder) GetMerchantById(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMerchantById", reflect.TypeOf((*MockStore)(nil).GetMerchantById), arg0, arg1)
}
// GetMerchantByUserId mocks base method.
func (m *MockStore) GetMerchantByUserId(arg0 context.Context, arg1 uuid.UUID) (db.Merchant, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetMerchantByUserId", arg0, arg1)
ret0, _ := ret[0].(db.Merchant)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetMerchantByUserId indicates an expected call of GetMerchantByUserId.
func (mr *MockStoreMockRecorder) GetMerchantByUserId(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMerchantByUserId", reflect.TypeOf((*MockStore)(nil).GetMerchantByUserId), arg0, arg1)
}
// GetProduct mocks base method.
func (m *MockStore) GetProduct(arg0 context.Context, arg1 uuid.UUID) (db.Product, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetProduct", arg0, arg1)
ret0, _ := ret[0].(db.Product)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetProduct indicates an expected call of GetProduct.
func (mr *MockStoreMockRecorder) GetProduct(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProduct", reflect.TypeOf((*MockStore)(nil).GetProduct), arg0, arg1)
}
// GetStockForUpdateStock mocks base method.
func (m *MockStore) GetStockForUpdateStock(arg0 context.Context, arg1 uuid.UUID) (db.Product, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetStockForUpdateStock", arg0, arg1)
ret0, _ := ret[0].(db.Product)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetStockForUpdateStock indicates an expected call of GetStockForUpdateStock.
func (mr *MockStoreMockRecorder) GetStockForUpdateStock(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStockForUpdateStock", reflect.TypeOf((*MockStore)(nil).GetStockForUpdateStock), arg0, arg1)
}
// ListProducts mocks base method.
func (m *MockStore) ListProducts(arg0 context.Context, arg1 db.ListProductsParams) ([]db.Product, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListProducts", arg0, arg1)
ret0, _ := ret[0].([]db.Product)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListProducts indicates an expected call of ListProducts.
func (mr *MockStoreMockRecorder) ListProducts(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProducts", reflect.TypeOf((*MockStore)(nil).ListProducts), arg0, arg1)
}
// PurchaseOrderTx mocks base method.
func (m *MockStore) PurchaseOrderTx(arg0 context.Context, arg1 db.PurchasoOrderTxParams) (db.PurchaseOrderTxResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PurchaseOrderTx", arg0, arg1)
ret0, _ := ret[0].(db.PurchaseOrderTxResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// PurchaseOrderTx indicates an expected call of PurchaseOrderTx.
func (mr *MockStoreMockRecorder) PurchaseOrderTx(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurchaseOrderTx", reflect.TypeOf((*MockStore)(nil).PurchaseOrderTx), arg0, arg1)
}
// SuppliersList mocks base method.
func (m *MockStore) SuppliersList(arg0 context.Context, arg1 db.SuppliersListParams) ([]db.Supplier, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SuppliersList", arg0, arg1)
ret0, _ := ret[0].([]db.Supplier)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SuppliersList indicates an expected call of SuppliersList.
func (mr *MockStoreMockRecorder) SuppliersList(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SuppliersList", reflect.TypeOf((*MockStore)(nil).SuppliersList), arg0, arg1)
}
// UpdateCustomer mocks base method.
func (m *MockStore) UpdateCustomer(arg0 context.Context, arg1 uuid.UUID) (db.Customer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateCustomer", arg0, arg1)
ret0, _ := ret[0].(db.Customer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateCustomer indicates an expected call of UpdateCustomer.
func (mr *MockStoreMockRecorder) UpdateCustomer(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCustomer", reflect.TypeOf((*MockStore)(nil).UpdateCustomer), arg0, arg1)
}
// UpdateProduct mocks base method.
func (m *MockStore) UpdateProduct(arg0 context.Context, arg1 db.UpdateProductParams) (db.Product, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateProduct", arg0, arg1)
ret0, _ := ret[0].(db.Product)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateProduct indicates an expected call of UpdateProduct.
func (mr *MockStoreMockRecorder) UpdateProduct(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateProduct", reflect.TypeOf((*MockStore)(nil).UpdateProduct), arg0, arg1)
}
// UpdateProductStock mocks base method.
func (m *MockStore) UpdateProductStock(arg0 context.Context, arg1 db.UpdateProductStockParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateProductStock", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateProductStock indicates an expected call of UpdateProductStock.
func (mr *MockStoreMockRecorder) UpdateProductStock(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateProductStock", reflect.TypeOf((*MockStore)(nil).UpdateProductStock), arg0, arg1)
}
// UpdateSupplier mocks base method.
func (m *MockStore) UpdateSupplier(arg0 context.Context, arg1 uuid.UUID) (db.Supplier, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateSupplier", arg0, arg1)
ret0, _ := ret[0].(db.Supplier)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateSupplier indicates an expected call of UpdateSupplier.
func (mr *MockStoreMockRecorder) UpdateSupplier(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSupplier", reflect.TypeOf((*MockStore)(nil).UpdateSupplier), arg0, arg1)
}

View File

@ -33,7 +33,7 @@ OFFSET $2;
-- name: UpdateProduct :one
UPDATE products
SET name = $2, selling_price = $3, purchase_price = $4, stock = $5, updated_at = $6
SET name = $2, selling_price = $3, purchase_price = $4, updated_at = $5
WHERE id = $1
RETURNING *;

19
db/query/users.sql Normal file
View File

@ -0,0 +1,19 @@
-- name: CreateUser :one
INSERT INTO users (
email,
password,
fullname
) VALUES ($1, $2, $3)
RETURNING *;
-- name: GetPasswordByEmail :one
SELECT password
FROM users
WHERE email = $1
RETURNING password;
-- name: GetUserById :one
SELECT *
FROM users
WHERE id = $1
RETURNING *;

View File

@ -65,7 +65,7 @@ func (q *Queries) CustomersList(ctx context.Context, arg CustomersListParams) ([
return nil, err
}
defer rows.Close()
var items []Customer
items := []Customer{}
for rows.Next() {
var i Customer
if err := rows.Scan(

View File

@ -129,7 +129,7 @@ func (q *Queries) ListProducts(ctx context.Context, arg ListProductsParams) ([]P
return nil, err
}
defer rows.Close()
var items []Product
items := []Product{}
for rows.Next() {
var i Product
if err := rows.Scan(
@ -158,7 +158,7 @@ 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, stock = $5, updated_at = $6
SET name = $2, selling_price = $3, purchase_price = $4, updated_at = $5
WHERE id = $1
RETURNING id, merchant_id, index_id, name, selling_price, purchase_price, stock, created_at, updated_at
`
@ -168,7 +168,6 @@ type UpdateProductParams struct {
Name string `json:"name"`
SellingPrice float64 `json:"selling_price"`
PurchasePrice float64 `json:"purchase_price"`
Stock float64 `json:"stock"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
@ -178,7 +177,6 @@ func (q *Queries) UpdateProduct(ctx context.Context, arg UpdateProductParams) (P
arg.Name,
arg.SellingPrice,
arg.PurchasePrice,
arg.Stock,
arg.UpdatedAt,
)
var i Product

View File

@ -13,9 +13,9 @@ import (
func createRandomProduct(t *testing.T) (Product, CreateProductParams) {
sellingPrice, _ := util.RandomFloat(999, 99999)
purchasePrice, _ := util.RandomFloat(999, 9999)
stock, _ := util.RandomFloat(10, 10000)
sellingPrice := util.RandomFloat(999, 99999)
purchasePrice := util.RandomFloat(999, 9999)
stock := util.RandomFloat(10, 10000)
arg := CreateProductParams{
MerchantID: uuid.MustParse("d9b0e126-991e-46ac-8c61-5efdd605f75d"),
@ -76,7 +76,6 @@ func TestUpdateProduct(t *testing.T) {
Name: util.RandomString(6),
SellingPrice: float64(200),
PurchasePrice: float64(200),
Stock: float64(200),
}
updatedProduct, err := testQueries.UpdateProduct(context.Background(), arg)
@ -88,7 +87,6 @@ func TestUpdateProduct(t *testing.T) {
require.Equal(t, arg.Name, updatedProduct.Name)
require.Equal(t, arg.PurchasePrice, updatedProduct.PurchasePrice)
require.Equal(t, arg.SellingPrice, updatedProduct.SellingPrice)
require.Equal(t, arg.Stock, updatedProduct.Stock)
require.NotSame(t, createProduct.Name, updatedProduct.Name)
require.NotSame(t, createProduct.SellingPrice, updatedProduct.SellingPrice)

35
db/sqlc/querier.go Normal file
View File

@ -0,0 +1,35 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.17.2
package db
import (
"context"
"github.com/google/uuid"
)
type Querier interface {
CreateCustomers(ctx context.Context, arg CreateCustomersParams) (Customer, error)
CreateProduct(ctx context.Context, arg CreateProductParams) (Product, error)
CreatePurchaseOrder(ctx context.Context, arg CreatePurchaseOrderParams) (PurchaseOrder, error)
CreatePurchaseOrderDetail(ctx context.Context, arg CreatePurchaseOrderDetailParams) (PurchaseOrderDetail, error)
CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error)
CustomersList(ctx context.Context, arg CustomersListParams) ([]Customer, error)
DeleteCustomer(ctx context.Context, id uuid.UUID) error
DeleteProduct(ctx context.Context, id uuid.UUID) error
DeleteSupplier(ctx context.Context, id uuid.UUID) error
GetMerchantById(ctx context.Context, id uuid.UUID) (Merchant, error)
GetMerchantByUserId(ctx context.Context, ownerID uuid.UUID) (Merchant, error)
GetProduct(ctx context.Context, id uuid.UUID) (Product, error)
GetStockForUpdateStock(ctx context.Context, id uuid.UUID) (Product, error)
ListProducts(ctx context.Context, arg ListProductsParams) ([]Product, error)
SuppliersList(ctx context.Context, arg SuppliersListParams) ([]Supplier, error)
UpdateCustomer(ctx context.Context, id uuid.UUID) (Customer, error)
UpdateProduct(ctx context.Context, arg UpdateProductParams) (Product, error)
UpdateProductStock(ctx context.Context, arg UpdateProductStockParams) error
UpdateSupplier(ctx context.Context, id uuid.UUID) (Supplier, error)
}
var _ Querier = (*Queries)(nil)

View File

@ -8,19 +8,24 @@ import (
"github.com/google/uuid"
)
type Store struct {
type Store interface {
Querier
PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error)
}
type SQLStore struct {
*Queries
db *sql.DB
}
func NewStore(db *sql.DB) *Store {
return &Store{
func NewStore(db *sql.DB) Store {
return &SQLStore{
db: db,
Queries: New(db),
}
}
func (store *Store) execTx(ctx context.Context, fn func(*Queries) error) error {
func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error {
tx, err := store.db.BeginTx(ctx, nil)
if err != nil {
return err
@ -62,7 +67,7 @@ type PurchaseOrderTxResult struct {
PurchaseOrderDetail []PurchaseOrderDetail `json:"detail"`
}
func (store *Store) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) {
func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) {
var result PurchaseOrderTxResult
err := store.execTx(ctx, func(q *Queries) error {

View File

@ -20,12 +20,12 @@ func TestPurchaseOrder(t *testing.T) {
errs := make(chan error)
results := make(chan PurchaseOrderTxResult)
purchaseProducts1Quantity, _ := util.RandomFloat(1, 99)
purchaseProducts1Price, _ := util.RandomFloat(999, 9999)
purchaseProducts1Quantity := util.RandomFloat(1, 99)
purchaseProducts1Price := util.RandomFloat(999, 9999)
purchaseProducts1SubTotal := purchaseProducts1Price * purchaseProducts1Quantity
purchaseProducts2Quantity, _ := util.RandomFloat(1, 99)
purchaseProducts2Price, _ := util.RandomFloat(999, 9999)
purchaseProducts2Quantity := util.RandomFloat(1, 99)
purchaseProducts2Price := util.RandomFloat(999, 9999)
purchaseProducts2SubTotal := purchaseProducts2Price * purchaseProducts2Quantity
purchaseProducts_1 := PurchaseOrderProduct{

View File

@ -73,7 +73,7 @@ func (q *Queries) SuppliersList(ctx context.Context, arg SuppliersListParams) ([
return nil, err
}
defer rows.Close()
var items []Supplier
items := []Supplier{}
for rows.Next() {
var i Supplier
if err := rows.Scan(

4
go.mod
View File

@ -4,9 +4,11 @@ go 1.20
require (
github.com/gin-gonic/gin v1.9.0
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
github.com/lib/pq v1.10.7
github.com/shopspring/decimal v1.3.1
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.2
github.com/tabbed/pqtype v0.1.1
)
@ -21,7 +23,6 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
@ -37,7 +38,6 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.15.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.10 // indirect

14
go.sum
View File

@ -61,6 +61,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -90,6 +91,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -116,10 +119,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -223,6 +224,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -274,6 +276,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -305,6 +308,7 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -326,6 +330,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -358,7 +363,9 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -424,12 +431,11 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=

View File

@ -7,5 +7,6 @@ packages:
engine: "postgresql"
emit_json_tags: true
emit_prepared_queries: false
emit_interface: false
emit_interface: true
emit_exact_table_names: false
emit_empty_slices: true

View File

@ -20,9 +20,10 @@ func RandomInt(min, max int64) int64 {
}
// RandomFloat with rounding for testing
func RandomFloat(min, max float64) (float64, bool) {
func RandomFloat(min, max float64) float64 {
val := (min + rand.Float64()*(max-min)) * 100
return decimal.NewFromFloat(val).Truncate(2).Float64()
val, _ = decimal.NewFromFloat(val).Truncate(2).Float64()
return val
}
// RandomString generates a random string of length n