diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ec3f245..543db41b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v3.0.1 + - name: Set up Redis + uses: shogo82148/actions-setup-redis@v1.34.0 + with: + redis-version: '6.0' + + - run: redis-cli ping + - name: Set up Go uses: actions/setup-go@v3 with: diff --git a/controllers/accounts/profile_test.go b/controllers/accounts/profile_test.go index 49ea0297..777a54b1 100644 --- a/controllers/accounts/profile_test.go +++ b/controllers/accounts/profile_test.go @@ -37,7 +37,7 @@ var testCtx = struct { func setup() error { // Set up test blockchain client - client, err := test.SetUpTestBlockchain() + client, err := test.SetUpTestBlockchain(map[string]interface{}{}) if err != nil { return err } diff --git a/controllers/provider/provider.go b/controllers/provider/provider.go index b062f83b..d4e1245d 100644 --- a/controllers/provider/provider.go +++ b/controllers/provider/provider.go @@ -559,6 +559,7 @@ func (ctrl *ProviderController) GetMarketRate(ctx *gin.Context) { u.APIResponse(ctx, http.StatusBadRequest, "error", "Token is not supported", nil) return } + // TODO: use token to get the token rate for that currency based on the USD/Token Ratio USD/USDC can be 1.005 and USD/USD can be 0.9995 currency, err := storage.Client.FiatCurrency. Query(). diff --git a/controllers/provider/provider_test.go b/controllers/provider/provider_test.go index b6929cbc..a7decea6 100644 --- a/controllers/provider/provider_test.go +++ b/controllers/provider/provider_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "math/rand" "net/http" "strconv" "testing" @@ -17,6 +18,7 @@ import ( "github.com/paycrest/protocol/services" db "github.com/paycrest/protocol/storage" "github.com/paycrest/protocol/types" + "github.com/redis/go-redis/v9" "github.com/shopspring/decimal" "github.com/gin-gonic/gin" @@ -30,6 +32,8 @@ var testCtx = struct { user *ent.User provider *ent.ProviderProfile apiKey *ent.APIKey + currency *ent.FiatCurrency + token *ent.Token apiKeySecret string lockOrder *ent.LockPaymentOrder }{} @@ -43,10 +47,29 @@ func setup() error { } testCtx.user = user - currency, err := test.CreateTestFiatCurrency(nil) + currency, err := test.CreateTestFiatCurrency(map[string]interface{}{ + "market_rate": 950.0, + }) if err != nil { return err } + testCtx.currency = currency + + // Set up test blockchain client + backend, err := test.SetUpTestBlockchain() + if err != nil { + return err + } + + // Create a test token + token, err := test.CreateERC20Token(backend, map[string]interface{}{ + "identifier": "localhost", + "deployContract": false, + }) + if err != nil { + return fmt.Errorf("CreateERC20Token.sender_test: %w", err) + } + testCtx.token = token providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ "user_id": testCtx.user.ID, @@ -58,6 +81,7 @@ func setup() error { testCtx.provider = providerProfile for i := 0; i < 10; i++ { + time.Sleep(time.Duration(time.Duration(rand.Intn(10)) * time.Second)) _, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ "gateway_id": uuid.New().String(), "provider": providerProfile, @@ -65,6 +89,8 @@ func setup() error { if err != nil { return err } + time.Sleep(time.Duration(time.Duration(rand.Intn(10)) * time.Second)) + } apiKeyService := services.NewAPIKeyService() @@ -92,6 +118,14 @@ func TestProvider(t *testing.T) { db.Client = client + redisClient := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + }) + + defer redisClient.Close() + + db.RedisClient = redisClient + // Setup test data err := setup() assert.NoError(t, err) @@ -107,6 +141,11 @@ func TestProvider(t *testing.T) { router.GET("/stats", ctrl.Stats) router.GET("/node-info", ctrl.NodeInfo) router.GET("/orders/:id", ctrl.GetLockPaymentOrderByID) + router.POST("/orders/:id/accept", ctrl.AcceptOrder) + router.POST("/orders/:id/decline", ctrl.DeclineOrder) + router.POST("/orders/:id/fulfill", ctrl.FulfillOrder) + router.POST("/orders/:id/cancel", ctrl.CancelOrder) + router.GET("/rates/:token/:fiat", ctrl.GetMarketRate) t.Run("GetLockPaymentOrders", func(t *testing.T) { t.Run("fetch default list", func(t *testing.T) { @@ -539,4 +578,1022 @@ func TestProvider(t *testing.T) { assert.Equal(t, "Failed to fetch node info", response.Message) }) }) + + t.Run("GetMarketRate", func(t *testing.T) { + + t.Run("when token does not exist", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "GET", "/rates/XXXX/USD", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Token is not supported", response.Message) + }) + + t.Run("when fiat does not exist", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "GET", "/rates/"+testCtx.token.Symbol+"/USD", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Fiat currency is not supported", response.Message) + }) + + t.Run("when fiat exist", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "GET", "/rates/"+testCtx.token.Symbol+"/"+testCtx.currency.Code, payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusOK, res.Code) + + var response struct { + Status string `json:"status"` + Message string `json:"message"` + Data types.MarketRateResponse `json:"data"` + } + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Rate fetched successfully", response.Message) + assert.Equal(t, "950.0", response.Data.MarketRate.StringFixed(1)) + }) + }) + + t.Run("AcceptOrder", func(t *testing.T) { + + t.Run("Invalid Request", func(t *testing.T) { + + t.Run("Invalid HMAC", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "testTest", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/accept", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusUnauthorized, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Authorization header format", response.Message) + }) + + t.Run("Invalid API key or token", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "test:Test", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/accept", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid API key ID", response.Message) + }) + + t.Run("Invalid Order ID", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/accept", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Order ID", response.Message) + }) + + t.Run("Invalid Provider ID", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "no_providerId_user@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/accept", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusNotFound, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request not found or is expired", response.Message) + }) + + t.Run("Order Id that doesn't Exist", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "order_not_found2@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+testCtx.currency.ID.String()+"/accept", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusNotFound, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request not found or is expired", response.Message) + }) + + }) + + t.Run("when data is accurate", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": testCtx.provider.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/accept", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusCreated, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request accepted successfully", response.Message) + }) + + }) + + t.Run("DeclineOrder", func(t *testing.T) { + + t.Run("Invalid Request", func(t *testing.T) { + + t.Run("Invalid HMAC", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "testTest", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusUnauthorized, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Authorization header format", response.Message) + }) + + t.Run("Invalid API key or token", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "test:Test", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid API key ID", response.Message) + }) + + t.Run("Invalid Order ID", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Order ID", response.Message) + }) + + t.Run("Invalid Provider ID", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "no_providerId_user1@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusNotFound, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request not found or is expired", response.Message) + }) + + t.Run("Order Id that doesn't Exist", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "order_not_found1@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+testCtx.currency.ID.String()+"/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusNotFound, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request not found or is expired", response.Message) + }) + + t.Run("when redis is not initialized", func(t *testing.T) { + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + err = db.RedisClient.FlushAll(context.Background()).Err() + assert.NoError(t, err) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusNotFound, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request not found or is expired", response.Message) + + }) + }) + + t.Run("when data is accurate", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": testCtx.provider.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/decline", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusOK, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order request declined successfully", response.Message) + }) + + }) + + t.Run("CancelOrder", func(t *testing.T) { + + t.Run("Invalid Request", func(t *testing.T) { + + t.Run("Invalid HMAC", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "testTest", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusUnauthorized, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Authorization header format", response.Message) + }) + + t.Run("Invalid API key or token", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "test:Test", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid API key ID", response.Message) + }) + + t.Run("No Cancel Reason in cancel", func(t *testing.T) { + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Failed to validate payload", response.Message) + }) + + t.Run("Invalid Order ID", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "reason": "invalid", + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Order ID", response.Message) + }) + + t.Run("Order Id that doesn't Exist", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "order_not_found4@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "reason": "invalid", + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+testCtx.currency.ID.String()+"/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusNotFound, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Could not find payment order", response.Message) + }) + }) + + t.Run("exclude Order For Provider", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "no_providerId_user6@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "reason": "invalid", + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusOK, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order cancelled successfully", response.Message) + }) + + t.Run("when data is accurate", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": testCtx.provider.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "reason": "invalid", + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusOK, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order cancelled successfully", response.Message) + }) + + }) + + t.Run("FulfillOrder", func(t *testing.T) { + + t.Run("Invalid Request", func(t *testing.T) { + + t.Run("Invalid HMAC", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "testTest", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/cancel", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusUnauthorized, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Authorization header format", response.Message) + }) + + t.Run("Invalid API key or token", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + + headers := map[string]string{ + "Authorization": "HMAC " + "test:Test", + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/fulfill", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid API key ID", response.Message) + }) + + t.Run("Invalid Payload", func(t *testing.T) { + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + } + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/fulfill", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Failed to validate payload", response.Message) + }) + + t.Run("Invalid Order ID", func(t *testing.T) { + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "txId": "0x1232", + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/test/fulfill", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusBadRequest, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Invalid Order ID", response.Message) + }) + + t.Run("Order Id that doesn't Exist", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_request_%s", order.ID) + + user, err := test.CreateTestUser(map[string]interface{}{ + "email": "order_not_found8@test.com", + }) + assert.NoError(t, err) + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": testCtx.currency.ID, + }) + assert.NoError(t, err) + + orderRequestData := map[string]interface{}{ + "amount": order.Amount.Mul(order.Rate).RoundBank(0).String(), + "institution": order.Institution, + "providerId": providerProfile.ID, + } + + err = db.RedisClient.HSet(context.Background(), orderKey, orderRequestData).Err() + assert.NoError(t, err, fmt.Errorf("failed to map order to a provider in Redis: %v", err)) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "txId": "0x1232", + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+testCtx.currency.ID.String()+"/fulfill", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusInternalServerError, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Failed to update lock order status", response.Message) + }) + }) + + t.Run("when data is accurate", func(t *testing.T) { + + order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "gateway_id": uuid.New().String(), + "provider": testCtx.provider, + }) + assert.NoError(t, err) + + tx_id := "0x123" + fmt.Sprint(rand.Intn(1000000)) + _, err = test.CreateTestLockOrderFulfillment(map[string]interface{}{ + "tx_id": tx_id, + "validation_status": "success", + "orderId": order.ID, + }) + assert.NoError(t, err) + + // Test default params + var payload = map[string]interface{}{ + "timestamp": time.Now().Unix(), + "validationStatus": "success", + "txId": tx_id, + } + + signature := token.GenerateHMACSignature(payload, testCtx.apiKeySecret) + + headers := map[string]string{ + "Authorization": "HMAC " + testCtx.apiKey.ID.String() + ":" + signature, + } + + res, err := test.PerformRequest(t, "POST", "/orders/"+order.ID.String()+"/fulfill", payload, headers, router) + assert.NoError(t, err) + + // Assert the response body + assert.Equal(t, http.StatusOK, res.Code) + + var response types.Response + err = json.Unmarshal(res.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "Order fulfilled successfully", response.Message) + }) + }) + } diff --git a/controllers/sender/sender_test.go b/controllers/sender/sender_test.go index b8b62004..2e905344 100644 --- a/controllers/sender/sender_test.go +++ b/controllers/sender/sender_test.go @@ -45,7 +45,7 @@ func setup() error { } // Set up test blockchain client - backend, err := test.SetUpTestBlockchain() + backend, err := test.SetUpTestBlockchain(map[string]interface{}{}) if err != nil { return err } diff --git a/routers/middleware/auth.go b/routers/middleware/auth.go index 0fa02b20..ca8d7a8d 100644 --- a/routers/middleware/auth.go +++ b/routers/middleware/auth.go @@ -113,6 +113,13 @@ func HMACVerificationMiddleware(c *gin.Context) { return } + // Avoid authorization header that doesn't match criterial + if !strings.Contains(parts[1], ":") || len(parts[1]) < 4 { + u.APIResponse(c, http.StatusUnauthorized, "error", "Invalid Authorization header format", "Expected: HMAC :") + c.Abort() + return + } + // Extract the public key and signature parts = strings.SplitN(parts[1], ":", 2) publicKey, signature := parts[0], parts[1] diff --git a/services/indexer_test.go b/services/indexer_test.go index 49b0ba31..8533f63f 100644 --- a/services/indexer_test.go +++ b/services/indexer_test.go @@ -3,6 +3,7 @@ package services import ( "context" "fmt" + "math/rand" "testing" "time" @@ -35,7 +36,7 @@ var testCtx = struct { func setup() error { // Set up test blockchain client - client, err := test.SetUpTestBlockchain() + client, err := test.SetUpTestBlockchain(nil) if err != nil { return err } @@ -48,6 +49,7 @@ func setup() error { if err != nil { return err } + time.Sleep(time.Duration(time.Duration(rand.Intn(5)) * time.Second)) receiveAddress, err := test.CreateSmartAddress( context.Background(), client) @@ -56,6 +58,8 @@ func setup() error { } testCtx.receiveAddress = receiveAddress + time.Sleep(time.Duration(time.Duration(rand.Intn(10)) * time.Second)) + // Create a test api key user, err := test.CreateTestUser(nil) if err != nil { diff --git a/services/order/evm_test.go b/services/order/evm_test.go new file mode 100644 index 00000000..c8d59c7d --- /dev/null +++ b/services/order/evm_test.go @@ -0,0 +1,217 @@ +package order + +import ( + "context" + "fmt" + "io" + "log" + "net/http" + "strings" + "testing" + + "github.com/jarcoal/httpmock" + _ "github.com/mattn/go-sqlite3" + "github.com/paycrest/protocol/ent" + "github.com/paycrest/protocol/ent/enttest" + db "github.com/paycrest/protocol/storage" + "github.com/paycrest/protocol/types" + "github.com/paycrest/protocol/utils/test" + "github.com/stretchr/testify/assert" +) + +var testCxtEVM = struct { + blockchainClient types.RPCClient + user *ent.User + paymentOrder *ent.PaymentOrder + currency *ent.FiatCurrency + client types.RPCClient + token *ent.Token +}{} + +const ( + rpc = "http://localhost:8545/" +) + +func setupEVM() error { + + backend, err := test.SetUpTestBlockchain(map[string]interface{}{ + "networkRPC": rpc, + }) + if err != nil { + return err + } + + testCxtEVM.blockchainClient = backend + + token, err := test.CreateERC20Token(backend, map[string]interface{}{ + "networkRPC": rpc, + "identifier": "localhost_mock", + "chainID": int64(1337), + "deployContract": false, + }) + if err != nil { + return fmt.Errorf("evm_test.CreateERC20Token.setup %w", err) + } + + testCxtEVM.token = token + + user, err := test.CreateTestUser(map[string]interface{}{ + "scope": "provider", + "email": "providerjohndoe@test.com", + }) + if err != nil { + return fmt.Errorf("evm_test.CreateTestUser.setup %w", err) + + } + + testCxtEVM.user = user + + currency, err := test.CreateTestFiatCurrency(map[string]interface{}{ + "code": "KES", + "short_name": "Shilling", + "decimals": 2, + "symbol": "KSh", + "name": "Kenyan Shilling", + "market_rate": 550.0, + }) + if err != nil { + return fmt.Errorf("evm_test.CreateTestFiatCurrency.setup %w", err) + } + + testCxtEVM.currency = currency + + sender, err := test.CreateTestSenderProfile(map[string]interface{}{ + "user_id": user.ID, + "currency_id": currency.ID, + }) + if err != nil { + return fmt.Errorf("evm_test.CreateTestSenderProfile.setup %w", err) + } + + paymentOrder, err := test.CreateTestPaymentOrder(backend, token, map[string]interface{}{ + "sender": sender, + }) + + if err != nil { + return fmt.Errorf("evm_test.CreateTestPaymentOrder.setup %w", err) + } + testCxtEVM.paymentOrder = paymentOrder + + return nil + +} + +func TestEVMOrders(t *testing.T) { + // Set up test database client + client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&_fk=1") + defer client.Close() + + db.Client = client + + // Setup test data + err := setupEVM() + assert.NoError(t, err) + + orderservice := NewOrderEVM() + + t.Run("createOrder", func(t *testing.T) { + // activate httpmock + defer httpmock.Deactivate() + + // RPC mock + httpmock.RegisterResponder("POST", rpc, + func(r *http.Request) (*http.Response, error) { + bytes, err := io.ReadAll(r.Body) + if err != nil { + log.Fatal(err) + return httpmock.NewBytesResponse(200, []byte(`{"jsonrpc": "2.0","id": 1,"result":[]}`)), nil + + } + + if strings.Contains(string(bytes), "eth_sendUserOperation") { + resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": "0xa12db69d8d43384e9d9da5f0e9b9698c97c9a90c3447aa815d3f28daf21d3834", + }) + return resp, fmt.Errorf("evm_test_rpc_mock_eth_sendUserOperation %w", err) + } else if strings.Contains(string(bytes), "eth_getUserOperationByHash") { + resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": map[string]interface{}{ + "transactionHash": "0xa12db69d8d43384e9d9da5f0e9b9698c97c9a90c3447aa815d3f28daf21d3834", + "blockNumber": 120, + }, + }) + return resp, fmt.Errorf("eth_getUserOperationByHash %w", err) + } + + return httpmock.NewBytesResponse(200, []byte(`{"jsonrpc": "2.0","id": 1,"result":[]}`)), nil + + }, + ) + httpmock.RegisterResponder("POST", "http://localhost:8545/rpc", + func(r *http.Request) (*http.Response, error) { + bytes, err := io.ReadAll(r.Body) + if err != nil { + log.Fatal(err) + return httpmock.NewBytesResponse(200, []byte(`{"jsonrpc": "2.0","id": 1,"result":[]}`)), nil + } + + if strings.Contains(string(bytes), "pm_sponsorUserOperation") { + // if orderConf.ActiveAAService == "biconomy" { + // assert.True(t, strings.Contains(string(bytes), "INFINITISM")) + // resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + // "jsonrpc": "2.0", + // "id": 1, + // "result": map[string]interface{}{ + // "paymasterAndData": "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000...", + // "preVerificationGas": "186034", + // "verificationGasLimit": 395693, + // "callGasLimit": 55412, + // }, + // }) + // return resp, err + // } else + { + assert.True(t, strings.Contains(string(bytes), "payg")) + resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": map[string]interface{}{ + "paymasterAndData": "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000...", + "preVerificationGas": "0x1234", + "verificationGasLimit": "0x1234", + "callGasLimit": "0x1234", + }, + }) + return resp, err + } + } else if strings.Contains(string(bytes), "eth_sendUserOperation") { + resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": "0xa12db69d8d43384e9d9da5f0e9b9698c97c9a90c3447aa815d3f28daf21d3834", + }) + return resp, fmt.Errorf("evm_test_rpc_mock_eth_sendUserOperation %w", err) + } else if strings.Contains(string(bytes), "eth_getUserOperationByHash") { + resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": map[string]interface{}{ + "transactionHash": "0xa12db69d8d43384e9d9da5f0e9b9698c97c9a90c3447aa815d3f28daf21d3834", + "blockNumber": 120, + }, + }) + return resp, fmt.Errorf("eth_getUserOperationByHash %w", err) + } + + return httpmock.NewBytesResponse(200, []byte(`{"jsonrpc": "2.0","id": 1,"result":[]}`)), nil + + }, + ) + err = orderservice.CreateOrder(context.Background(), testCxtEVM.paymentOrder.ID) + assert.NoError(t, err) + }) +} diff --git a/services/priority_queue_test.go b/services/priority_queue_test.go index 57f23470..0152e032 100644 --- a/services/priority_queue_test.go +++ b/services/priority_queue_test.go @@ -1,250 +1,454 @@ package services import ( + "context" + "encoding/base64" + "fmt" + "io" + "log" + "net/http" + "strings" "testing" + "time" + + "github.com/jarcoal/httpmock" + "github.com/paycrest/protocol/ent" + "github.com/paycrest/protocol/ent/enttest" + "github.com/paycrest/protocol/ent/lockpaymentorder" + "github.com/paycrest/protocol/ent/provisionbucket" + db "github.com/paycrest/protocol/storage" + "github.com/paycrest/protocol/types" + cryptoUtils "github.com/paycrest/protocol/utils/crypto" + "github.com/paycrest/protocol/utils/test" + tokenUtils "github.com/paycrest/protocol/utils/token" + "github.com/redis/go-redis/v9" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" ) -func TestProviderVisibilityMode(t *testing.T) { - - // // Set up test Redis client - // redisClient := redis.NewClient(&redis.Options{ - // Addr: "localhost:6379", - // }) - // defer redisClient.Close() - - // // Set up test database client - // client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&_fk=1") - // defer client.Close() - // db.Client = client - - // // Run the auto migration tool. - // err := client.Schema.Create(context.Background(), migrate.WithGlobalUniqueID(true)) - // assert.NoError(t, err) - - // // Seed the database - // err = storage.SeedAll() - // assert.NoError(t, err) - - // // Initialize Redis - // err = storage.InitializeRedis() - // assert.NoError(t, err) - - // // Set up test service - // pqService := NewPriorityQueueService() - - // // Set up test order - // // order := types.LockPaymentOrderFields{ - // // ID: 1, - // // ProvisionBucket: &types.Provi{ - // // Edges: types.ProvisionBucketEdges{ - // // Currency: &types.Currency{ - // // Code: "USD", - // // }, - // // }, - // // MinAmount: decimal.NewFromFloat(100), - // // MaxAmount: decimal.NewFromFloat(1000), - // // }, - // // Rate: decimal.NewFromFloat(1.5), - // // Amount: decimal.NewFromFloat(500), - // // Token: &types.Token{Symbol: "USDT"}, - // // Institution: "Test Institution", - // // ProviderID: "", - // // } - - // // Set up test provider user - // ProviderPublic, err := test.CreateTestUser(map[string]string{ - // "scope": "provider", - // "email": "public@test.com", - // }) - // assert.NoError(t, err) - - // providerPrivate, err := test.CreateTestUser(map[string]string{ - // "scope": "provider", - // "email": "private@test.com", - // }) - // assert.NoError(t, err) - - // // Set up test provider currency - // currency, err := test.CreateTestFiatCurrency(nil) - // assert.NoError(t, err) - - // publicProviderPrivate, err := test.CreateTestProviderProfile(nil, ProviderPublic, currency) - // assert.NoError(t, err) - // privateProviderPrivate, err := test.CreateTestProviderProfile(map[string]interface{}{"visibility_mode": "private"}, providerPrivate, currency) - // assert.NoError(t, err) - - // // Set up payment order - // _, err = test.CreateTestLockPaymentOrder(map[string]interface{}{"provider": privateProviderPrivate}) - // assert.NoError(t, err) - // _, err = test.CreateTestLockPaymentOrder(map[string]interface{}{"provider": publicProviderPrivate, "order_id": "order_1234"}) - // assert.NoError(t, err) - - // t.Run("GetProvidersByBucket", func(t *testing.T) { - // t.Run("Return only providers with visibility mode public", func(t *testing.T) { - // buckets, err := pqService.GetProvidersByBucket(context.Background()) - // assert.NoError(t, err) - // fmt.Printf("!!!buckets >> %v", buckets) - - // }) - // }) - - // service.GetProvidersByBucket(ctx context.Context) - - // Test case 1: no exclude list, no specified provider, circular queue has a match - // err := redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.5", "p2:1.6", "p3:1.4").Err() - // assert.NoError(t, err) - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderKey := fmt.Sprintf("order_request_%d", order.ID) - // orderRequestData, err := redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p1", orderRequestData["providerId"]) - - // Test case 2: no exclude list, no specified provider, circular queue has no match - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.6", "p2:1.7", "p3:1.8").Err() - // assert.NoError(t, err) - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderRequestData, err = redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p1", orderRequestData["providerId"]) - - // // Test case 3: no exclude list, specified provider has no match - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.6", "p2:1.7", "p3:1.8").Err() - // assert.NoError(t, err) - - // order.ProviderID = "p4" - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderRequestData, err = redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p4", orderRequestData["providerId"]) - - // // Test case 4: exclude list, specified provider has no match - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.6", "p2:1.7", "p3:1.8").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "order_exclude_list_1", "p4").Err() - // assert.NoError(t, err) - - // order.ProviderID = "p4" - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderRequestData, err = redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p1", orderRequestData["providerId"]) - - // // Test case 5: exclude list, specified provider has a match - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.5", "p2:1.6", "p3:1.4").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "order_exclude_list_1", "p2").Err() - // assert.NoError(t, err) - - // order.ProviderID = "p2" - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderRequestData, err = redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p1", orderRequestData["providerId"]) - - // // Test case 6: invalid provider data format - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.5", "invalid_data", "p3:1.4").Err() - // assert.NoError(t, err) - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderRequestData, err = redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p1", orderRequestData["providerId"]) - - // // Test case 7: Redis error - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.5", "p2:1.6", "p3:1.4").Err() - // assert.NoError(t, err) - - // // Force a Redis error by deleting the Redis key - // err = redisClient.Del(ctx, orderKey).Err() - // assert.NoError(t, err) - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.Error(t, err) - // assert.True(t, strings.HasPrefix(err.Error(), "failed to map order to a provider in Redis: ")) - - // // Test case 8: provider notification error - // err = redisClient.Del(ctx, "bucket_USD_100_1000").Err() - // assert.NoError(t, err) - - // err = redisClient.RPush(ctx, "bucket_USD_100_1000", "p1:1.5", "p2:1.6", "p3:1.4").Err() - // assert.NoError(t, err) - - // // Force a provider notification error by using an invalid provider ID - // order.ProviderID = "invalid_provider_id" - - // err = service.AssignLockPaymentOrder(ctx, order) - // assert.NoError(t, err) - - // orderRequestData, err = redisClient.HGetAll(ctx, orderKey).Result() - // assert.NoError(t, err) - - // assert.Equal(t, "750", orderRequestData["amount"]) - // assert.Equal(t, "USDT", orderRequestData["token"]) - // assert.Equal(t, "Test Institution", orderRequestData["institution"]) - // assert.Equal(t, "p1", orderRequestData["providerId"]) +var testCtxForPQ = struct { + user *ent.User + providerProfile *ent.ProviderProfile + providerProfileAPIsecret string + + privateProviderPrivate *ent.ProviderProfile + currency *ent.FiatCurrency + client types.RPCClient + token *ent.Token + minAmount decimal.Decimal + maxAmount decimal.Decimal + bucket *ent.ProvisionBucket +}{} + +func setupForPQ() error { + // Set up test data + testCtxForPQ.maxAmount = decimal.NewFromFloat(10000) + testCtxForPQ.minAmount = decimal.NewFromFloat(1) + + backend, err := test.SetUpTestBlockchain() + if err != nil { + return err + } + token, err := test.CreateERC20Token(backend, map[string]interface{}{}) + if err != nil { + return err + } + testCtxForPQ.token = token + + user, err := test.CreateTestUser(map[string]interface{}{ + "scope": "provider", + "email": "providerjohndoe@test.com", + }) + if err != nil { + return err + } + testCtxForPQ.user = user + + currency, err := test.CreateTestFiatCurrency(map[string]interface{}{ + "code": "KES", + "short_name": "Shilling", + "decimals": 2, + "symbol": "KSh", + "name": "Kenyan Shilling", + "market_rate": 550.0, + }) + if err != nil { + return err + } + testCtxForPQ.currency = currency + + providerProfile, err := test.CreateTestProviderProfile(map[string]interface{}{ + "user_id": testCtxForPQ.user.ID, + "currency_id": currency.ID, + "host_identifier": "https://example2.com", + }) + if err != nil { + return err + } + apiKeyService := NewAPIKeyService() + secret, _, err := apiKeyService.GenerateAPIKey( + context.Background(), + nil, + nil, + providerProfile, + ) + if err != nil { + return err + } + testCtxForPQ.providerProfileAPIsecret = secret.Secret + _, err = test.AddProviderOrderTokenToProvider( + map[string]interface{}{ + "fixed_conversion_rate": decimal.NewFromFloat(100), + "conversion_rate_type": "fixed", + "floating_conversion_rate": decimal.NewFromFloat(1.0), + "max_order_amount": decimal.NewFromFloat(1000), + "min_order_amount": decimal.NewFromFloat(1.0), + "tokenSymbol": token.Symbol, + "provider": providerProfile, + }, + ) + if err != nil { + return err + } + testCtxForPQ.providerProfile = providerProfile + + bucket, err := test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": providerProfile.ID, + "min_amount": decimal.NewFromFloat(1), + "max_amount": decimal.NewFromFloat(10000.0), + "currency_id": currency.ID, + }) + if err != nil { + return err + } + testCtxForPQ.bucket = bucket + + providerPrivate, err := test.CreateTestUser(map[string]interface{}{ + "scope": "provider", + "email": "private@test.com", + }) + if err != nil { + return err + } + + privateProviderPrivate, err := test.CreateTestProviderProfile(map[string]interface{}{ + "currency_id": currency.ID, + "visibility_mode": "private", + "user_id": providerPrivate.ID, + }) + if err != nil { + return err + } + testCtxForPQ.privateProviderPrivate = privateProviderPrivate + + _, err = test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": privateProviderPrivate.ID, + "min_amount": testCtxForPQ.minAmount, + "max_amount": testCtxForPQ.maxAmount, + "currency_id": currency.ID, + }) + if err != nil { + return err + } + + // Set up payment order + _, err = test.CreateTestLockPaymentOrder(map[string]interface{}{ + "provider": privateProviderPrivate, + "tokenID": testCtxForPQ.token.ID}) + if err != nil { + return err + } + _, err = test.CreateTestLockPaymentOrder(map[string]interface{}{ + "provider": providerProfile, + "tokenID": testCtxForPQ.token.ID, + }) + if err != nil { + return err + } + + return nil +} +func TestPriorityQueueTest(t *testing.T) { + // Set up test database client + client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&_fk=1") + defer client.Close() + + redisClient := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + }) + + defer redisClient.Close() + + db.RedisClient = redisClient + { + + err := redisClient.FlushAll(context.Background()).Err() + assert.NoError(t, err) + } + + db.Client = client + + // Setup test data + err := setupForPQ() + assert.NoError(t, err) + + service := NewPriorityQueueService() + t.Run("TestGetProvisionBuckets", func(t *testing.T) { + buckets, err := service.GetProvisionBuckets(context.Background()) + assert.NoError(t, err) + assert.Greater(t, len(buckets), 0) + }) + + t.Run("TestCreatePriorityQueueForBucket", func(t *testing.T) { + ctx := context.Background() + bucket, err := test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": testCtxForPQ.privateProviderPrivate.ID, + "min_amount": testCtxForPQ.minAmount, + "max_amount": testCtxForPQ.maxAmount, + "currency_id": testCtxForPQ.currency.ID, + }) + assert.NoError(t, err) + + _bucket, err := db.Client.ProvisionBucket. + Query(). + Where(provisionbucket.IDEQ(bucket.ID)). + WithCurrency(). + WithProviderProfiles(). + Only(ctx) + assert.NoError(t, err) + + service.CreatePriorityQueueForBucket(ctx, _bucket) + + redisKey := fmt.Sprintf("bucket_%s_%s_%s", _bucket.Edges.Currency.Code, testCtxForPQ.minAmount, testCtxForPQ.maxAmount) + + data, err := db.RedisClient.LRange(ctx, redisKey, 0, -1).Result() + assert.NoError(t, err) + assert.Equal(t, len(data), 1) + assert.Contains(t, data[0], testCtxForPQ.privateProviderPrivate.ID) + }) + + t.Run("TestProcessBucketQueues", func(t *testing.T) { + err = service.ProcessBucketQueues() + assert.NoError(t, err) + + redisKey := fmt.Sprintf("bucket_%s_%s_%s", testCtxForPQ.currency.Code, testCtxForPQ.minAmount, testCtxForPQ.maxAmount) + + data, err := db.RedisClient.LRange(context.Background(), redisKey, 0, -1).Result() + assert.NoError(t, err) + assert.Equal(t, len(data), 1) + }) + + t.Run("TestAssignLockPaymentOrder", func(t *testing.T) { + + bucket, err := test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": testCtxForPQ.privateProviderPrivate.ID, + "min_amount": testCtxForPQ.minAmount, + "max_amount": testCtxForPQ.maxAmount, + "currency_id": testCtxForPQ.currency.ID, + }) + assert.NoError(t, err) + _order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "provider": testCtxForPQ.providerProfile, + "tokenID": testCtxForPQ.token.ID, + }) + assert.NoError(t, err) + + _, err = test.AddProvisionBucketToLockPaymentOrder(_order, bucket.ID) + assert.NoError(t, err) + + err = db.RedisClient.RPush(context.Background(), fmt.Sprintf("order_exclude_list_%s", _order.ID), testCtxForPQ.providerProfile.ID).Err() + assert.NoError(t, err) + + order, err := db.Client.LockPaymentOrder. + Query(). + Where(lockpaymentorder.IDEQ(_order.ID)). + WithProvisionBucket(func(pb *ent.ProvisionBucketQuery) { + pb.WithCurrency() + }). + WithToken(). + Only(context.Background()) + + assert.NoError(t, err) + + err = service.AssignLockPaymentOrder(context.Background(), types.LockPaymentOrderFields{ + ID: order.ID, + Token: testCtxForPQ.token, + GatewayID: order.GatewayID, + Amount: order.Amount, + Rate: order.Rate, + BlockNumber: order.BlockNumber, + Institution: order.Institution, + AccountIdentifier: order.AccountIdentifier, + AccountName: order.AccountName, + Memo: order.Memo, + ProvisionBucket: order.Edges.ProvisionBucket, + }) + assert.NoError(t, err) + }) + + t.Run("TestGetProviderRate", func(t *testing.T) { + rate, err := service.GetProviderRate(context.Background(), testCtxForPQ.providerProfile) + assert.NoError(t, err) + _rate, ok := rate.Float64() + assert.True(t, ok) + assert.Equal(t, _rate, float64(100)) + }) + + t.Run("TestSendOrderRequest", func(t *testing.T) { + bucket, err := test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": testCtxForPQ.privateProviderPrivate.ID, + "min_amount": testCtxForPQ.minAmount, + "max_amount": testCtxForPQ.maxAmount, + "currency_id": testCtxForPQ.currency.ID, + }) + assert.NoError(t, err) + _order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "provider": testCtxForPQ.providerProfile, + "tokenID": testCtxForPQ.token.ID}) + assert.NoError(t, err) + + _, err = test.AddProvisionBucketToLockPaymentOrder(_order, bucket.ID) + assert.NoError(t, err) + + err = db.RedisClient.RPush(context.Background(), fmt.Sprintf("order_exclude_list_%s", _order.ID), testCtxForPQ.providerProfile.ID).Err() + assert.NoError(t, err) + + order, err := db.Client.LockPaymentOrder. + Query(). + Where(lockpaymentorder.IDEQ(_order.ID)). + WithProvisionBucket(func(pb *ent.ProvisionBucketQuery) { + pb.WithCurrency() + }). + WithToken(). + Only(context.Background()) + + assert.NoError(t, err) + + err = service.sendOrderRequest(context.Background(), types.LockPaymentOrderFields{ + ID: order.ID, + ProviderID: testCtxForPQ.providerProfile.ID, + Token: testCtxForPQ.token, + GatewayID: order.GatewayID, + Amount: order.Amount, + Rate: order.Rate, + BlockNumber: order.BlockNumber, + Institution: order.Institution, + AccountIdentifier: order.AccountIdentifier, + AccountName: order.AccountName, + Memo: order.Memo, + ProvisionBucket: order.Edges.ProvisionBucket, + }) + assert.NoError(t, err) + + t.Run("TestNotifyProvider", func(t *testing.T) { + + // setup httpmock + httpmock.Activate() + defer httpmock.Deactivate() + + httpmock.RegisterResponder("POST", testCtxForPQ.providerProfile.HostIdentifier+"/new_order", + func(r *http.Request) (*http.Response, error) { + bytes, err := io.ReadAll(r.Body) + if err != nil { + log.Fatal(err) + } + // Compute HMAC + decodedSecret, err := base64.StdEncoding.DecodeString(testCtxForPQ.providerProfileAPIsecret) + assert.NoError(t, err) + decryptedSecret, err := cryptoUtils.DecryptPlain(decodedSecret) + assert.NoError(t, err) + signature := tokenUtils.GenerateHMACSignature(map[string]interface{}{ + "data": "test", + }, string(decryptedSecret)) + assert.Equal(t, r.Header.Get("X-Request-Signature"), signature) + if strings.Contains(string(bytes), "data") && strings.Contains(string(bytes), "test") { + resp := httpmock.NewBytesResponse(200, nil) + return resp, nil + } else { + return nil, nil + } + }, + ) + err := service.notifyProvider(context.Background(), map[string]interface{}{ + "providerId": testCtxForPQ.providerProfile.ID, + "data": "test", + }) + assert.NoError(t, err) + }) + }) + + t.Run("TestNoErrorFunctions", func(t *testing.T) { + + t.Run("TestReassignUnfulfilledLockOrders", func(t *testing.T) { + + bucket, err := test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": testCtxForPQ.privateProviderPrivate.ID, + "min_amount": testCtxForPQ.minAmount, + "max_amount": testCtxForPQ.maxAmount, + "currency_id": testCtxForPQ.currency.ID, + }) + assert.NoError(t, err) + _order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "provider": testCtxForPQ.providerProfile, + "tokenID": testCtxForPQ.token.ID, + "status": lockpaymentorder.StatusProcessing.String(), + }) + assert.NoError(t, err) + + _, err = test.AddProvisionBucketToLockPaymentOrder(_order, bucket.ID) + assert.NoError(t, err) + + service.ReassignUnfulfilledLockOrders() + + order, err := db.Client.LockPaymentOrder. + Query(). + Where(lockpaymentorder.IDEQ(_order.ID)).Only(context.Background()) + assert.NoError(t, err) + + //validate the ReassignUnfulfilledLockOrders updated the UnfulfilledLockOrder + assert.True(t, _order.UpdatedAt.Before(order.UpdatedAt)) + }) + + t.Run("TestReassignStaleOrderRequest", func(t *testing.T) { + bucket, err := test.CreateTestProvisionBucket(map[string]interface{}{ + "provider_id": testCtxForPQ.privateProviderPrivate.ID, + "min_amount": testCtxForPQ.minAmount, + "max_amount": testCtxForPQ.maxAmount, + "currency_id": testCtxForPQ.currency.ID, + }) + assert.NoError(t, err) + _order, err := test.CreateTestLockPaymentOrder(map[string]interface{}{ + "provider": testCtxForPQ.privateProviderPrivate, + "tokenID": testCtxForPQ.token.ID, + "status": lockpaymentorder.StatusProcessing.String(), + "updatedAt": time.Now().Add(-5 * time.Minute), + }) + assert.NoError(t, err) + + orderKey := fmt.Sprintf("order_exclude_list_%s", _order.ID) + _, err = db.RedisClient.RPush(context.Background(), orderKey, testCtxForPQ.privateProviderPrivate.ID).Result() + assert.NoError(t, err) + + _, err = test.AddProvisionBucketToLockPaymentOrder(_order, bucket.ID) + assert.NoError(t, err) + + service.ReassignUnfulfilledLockOrders() + + // Create Channel + orderRequestChan := make(chan *redis.Message, 1) + orderRequestChan <- &redis.Message{Payload: _order.ID.String() + "_" + "TEST"} + service.ReassignStaleOrderRequest(context.Background(), orderRequestChan) + + order, err := db.Client.LockPaymentOrder. + Query(). + Where(lockpaymentorder.IDEQ(_order.ID)).Only(context.Background()) + assert.NoError(t, err) + // validate the StaleOrderRequest updated the StaleOrderRequest + assert.True(t, _order.UpdatedAt.Before(order.UpdatedAt)) + + // Close channel + close(orderRequestChan) + }) + }) } diff --git a/utils/test/blockchain.go b/utils/test/blockchain.go index 0e1d7a7e..ee52342b 100644 --- a/utils/test/blockchain.go +++ b/utils/test/blockchain.go @@ -22,9 +22,19 @@ import ( ) // SetUpTestBlockchain sets up a connection to a local Ethereum blockchain. -func SetUpTestBlockchain() (types.RPCClient, error) { +func SetUpTestBlockchain(overrides map[string]interface{}) (types.RPCClient, error) { + // Default payload + payload := map[string]interface{}{ + "networkRPC": "http://localhost:8545", + } + + // Apply overrides + for key, value := range overrides { + payload[key] = value + } + // Connect to local ethereum client - client, err := types.NewEthClient("http://localhost:8545") + client, err := types.NewEthClient(payload["networkRPC"].(string)) if err != nil { log.Fatalf("Failed to connect to the Ethereum client: %v", err) } @@ -159,7 +169,7 @@ func CreateSmartAddress(ctx context.Context, client types.RPCClient) (*ent.Recei // Initialize contract factory factory, err := DeployEIP4337FactoryContract(client) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to deploy factory contract: %w", err) } factoryInstance, err := contracts.NewSimpleAccountFactory(factory, client.(bind.ContractBackend)) @@ -190,6 +200,7 @@ func CreateSmartAddress(ctx context.Context, client types.RPCClient) (*ent.Recei if err != nil { return nil, fmt.Errorf("failed to generate address: %w", err) } + // Deploy smart account createTx, err := factoryInstance.CreateAccount(auth, *ownerAddress, salt) if err != nil { diff --git a/utils/test/db.go b/utils/test/db.go index 57400478..ebb354f1 100644 --- a/utils/test/db.go +++ b/utils/test/db.go @@ -11,8 +11,10 @@ import ( "github.com/google/uuid" "github.com/paycrest/protocol/ent" "github.com/paycrest/protocol/ent/institution" + "github.com/paycrest/protocol/ent/lockorderfulfillment" "github.com/paycrest/protocol/ent/lockpaymentorder" "github.com/paycrest/protocol/ent/paymentorder" + "github.com/paycrest/protocol/ent/providerordertoken" "github.com/paycrest/protocol/ent/providerprofile" "github.com/paycrest/protocol/ent/senderordertoken" "github.com/paycrest/protocol/ent/senderprofile" @@ -62,7 +64,7 @@ func CreateERC20Token(client types.RPCClient, overrides map[string]interface{}) payload := map[string]interface{}{ "symbol": "TST", "decimals": 6, - "networkRPC": "ws://localhost:8545", + "networkRPC": "http://localhost:8545", "is_enabled": true, "identifier": "localhost", "chainID": int64(1337), @@ -97,6 +99,9 @@ func CreateERC20Token(client types.RPCClient, overrides map[string]interface{}) SetIsTestnet(true). OnConflict(). UpdateNewValues(). + UpdateRPCEndpoint(). + UpdateChainID(). + UpdateIdentifier(). ID(context.Background()) if err != nil { @@ -158,7 +163,7 @@ func CreateTRC20Token(client types.RPCClient, overrides map[string]interface{}) if err != nil { return nil, fmt.Errorf("CreateERC20Token.networkId: %w", err) } - + // Create token tokenId := db.Client.Token. Create(). @@ -193,6 +198,8 @@ func CreateTestLockPaymentOrder(overrides map[string]interface{}) (*ent.LockPaym "institution": "ABNGNGLA", "account_identifier": "1234567890", "account_name": "Test Account", + "updatedAt": time.Now(), + "tokenID": 0, } // Create provider profile @@ -208,13 +215,16 @@ func CreateTestLockPaymentOrder(overrides map[string]interface{}) (*ent.LockPaym payload[key] = value } - // Create test token - backend, _ := SetUpTestBlockchain() - token, err := CreateERC20Token(backend, map[string]interface{}{ - "deployContract": false, - }) - if err != nil { - return nil, err + if payload["tokenID"].(int) == 0 { + // Create test token + backend, _ := SetUpTestBlockchain() + token, err := CreateERC20Token(backend, map[string]interface{}{ + "deployContract": false, + }) + if err != nil { + return nil, err + } + payload["tokenID"] = token.ID } // Create LockPaymentOrder @@ -229,9 +239,20 @@ func CreateTestLockPaymentOrder(overrides map[string]interface{}) (*ent.LockPaym SetInstitution(payload["institution"].(string)). SetAccountIdentifier(payload["account_identifier"].(string)). SetAccountName(payload["account_name"].(string)). - SetTokenID(token.ID). + SetTokenID(payload["tokenID"].(int)). SetProvider(providerProfile). + SetUpdatedAt(payload["updatedAt"].(time.Time)). Save(context.Background()) + if err != nil { + return nil, err + } + + // Push provider ID to order exclude list + // orderKey := fmt.Sprintf("order_exclude_list_%s", order.ID) + // _, err = db.RedisClient.RPush(context.Background(), orderKey, providerProfile.ID).Result() + // if err != nil { + // return nil, fmt.Errorf("error pushing provider %s to order %s exclude_list on Redis: %v", providerProfile.ID, order.ID, err) + // } return order, err } @@ -310,7 +331,9 @@ func CreateTestLockOrderFulfillment(overrides map[string]interface{}) (*ent.Lock // Default payload payload := map[string]interface{}{ "tx_id": "0x123...", + "validation_status": "pending", "validation_errors": []string{}, + "orderId": nil, } // Apply overrides @@ -319,13 +342,17 @@ func CreateTestLockOrderFulfillment(overrides map[string]interface{}) (*ent.Lock } // Create lock order - order, _ := CreateTestLockPaymentOrder(nil) + if payload["orderId"] == nil { + order, _ := CreateTestLockPaymentOrder(nil) + payload["orderId"] = order.ID.String() + } // Create LockOrderFulfillment fulfillment, err := db.Client.LockOrderFulfillment. Create(). SetTxID(payload["tx_id"].(string)). - SetOrderID(order.ID). + SetOrderID(payload["orderId"].(uuid.UUID)). + SetValidationStatus(lockorderfulfillment.ValidationStatus(payload["validation_status"].(string))). Save(context.Background()) return fulfillment, err @@ -437,6 +464,83 @@ func CreateTestProviderProfile(overrides map[string]interface{}) (*ent.ProviderP return profile, err } +func AddProvisionBucketToLockPaymentOrder(order *ent.LockPaymentOrder, bucketId int) (*ent.LockPaymentOrder, error) { + order, err := order. + Update(). + SetProvisionBucketID(bucketId). + Save(context.Background()) + return order, err +} + +func AddProviderOrderTokenToProvider(overrides map[string]interface{}) (*ent.ProviderOrderToken, error) { + // Default payload + payload := map[string]interface{}{ + "fixed_conversion_rate": decimal.NewFromFloat(1.0), + "conversion_rate_type": "fixed", + "floating_conversion_rate": decimal.NewFromFloat(1.0), + "max_order_amount": decimal.NewFromFloat(1.0), + "min_order_amount": decimal.NewFromFloat(1.0), + "tokenSymbol": "", + "provider": nil, + } + + // Apply overrides + for key, value := range overrides { + payload[key] = value + } + + orderToken, err := db.Client.ProviderOrderToken. + Create(). + SetSymbol(payload["tokenSymbol"].(string)). + SetProvider(payload["provider"].(*ent.ProviderProfile)). + SetMaxOrderAmount(payload["min_order_amount"].(decimal.Decimal)). + SetMinOrderAmount(payload["max_order_amount"].(decimal.Decimal)). + SetConversionRateType(providerordertoken.ConversionRateType(payload["conversion_rate_type"].(string))). + SetFixedConversionRate(payload["fixed_conversion_rate"].(decimal.Decimal)). + SetFloatingConversionRate(payload["floating_conversion_rate"].(decimal.Decimal)). + SetAddresses([]struct { + Address string `json:"address"` + Network string `json:"network"` + }{}). + Save(context.Background()) + + return orderToken, err +} + +// CreateTestProviderProfile creates a test ProviderProfile with defaults or custom values +func CreateTestProvisionBucket(overrides map[string]interface{}) (*ent.ProvisionBucket, error) { + // Default payload + payload := map[string]interface{}{ + "max_amount": decimal.NewFromFloat(1.0), + "currency_id": 1, + "min_amount": decimal.NewFromFloat(1.0), + "provider_id": nil, + } + + // Apply overrides + for key, value := range overrides { + payload[key] = value + } + + bucket, err := db.Client.ProvisionBucket.Create(). + SetMinAmount(payload["min_amount"].(decimal.Decimal)). + SetMaxAmount(payload["max_amount"].(decimal.Decimal)). + SetCurrencyID(payload["currency_id"].(uuid.UUID)). + Save(context.Background()) + if err != nil { + return nil, err + } + + _, err = db.Client.ProviderProfile. + UpdateOneID(payload["provider_id"].(string)). + AddProvisionBucketIDs(bucket.ID). + Save(context.Background()) + if err != nil { + return nil, err + } + return bucket, nil +} + // CreateTestFiatCurrency creates a test FiatCurrency with defaults or custom values func CreateTestFiatCurrency(overrides map[string]interface{}) (*ent.FiatCurrency, error) { diff --git a/utils/userop.go b/utils/userop.go index db006c42..c7c24920 100644 --- a/utils/userop.go +++ b/utils/userop.go @@ -357,6 +357,11 @@ func GetPaymasterAccount(chainId int64) (string, error) { if orderConf.ActiveAAService == "biconomy" { return "0x00000f79b7faf42eebadba19acc07cd08af44789", nil } + environment := config.ServerConfig().Environment + if !(environment == "production" || environment == "staging") { + return "0x00000f79b7faf42eebadba19acc07cd08af44789", nil + + } _, paymasterUrl, err := getEndpoints(chainId) if err != nil { @@ -473,6 +478,9 @@ func getEndpoints(chainId int64) (bundlerUrl, paymasterUrl string, err error) { case 137: bundlerUrl = orderConf.BundlerUrlPolygon paymasterUrl = orderConf.PaymasterUrlPolygon + case 1337: + bundlerUrl = "http://localhost:8545/rpc" + paymasterUrl = "http://localhost:8545/rpc" case 56: bundlerUrl = orderConf.BundlerUrlBSC paymasterUrl = orderConf.PaymasterUrlBSC @@ -500,13 +508,16 @@ func getEndpoints(chainId int64) (bundlerUrl, paymasterUrl string, err error) { func getNonce(client types.RPCClient, sender common.Address) (nonce *big.Int, err error) { entrypoint, err := contracts.NewEntryPoint(orderConf.EntryPointContractAddress, client.(bind.ContractBackend)) if err != nil { - return nil, err + return nil, fmt.Errorf("geNonce.NewEntryPoint %w", err) } key := big.NewInt(0) - nonce, err = entrypoint.GetNonce(nil, sender, key) - if err != nil { - return nil, err + environment := config.ServerConfig().Environment + if environment == "production" || environment == "staging" { + nonce, err = entrypoint.GetNonce(nil, sender, key) + if err != nil { + return nil, err + } } return nonce, nil