From 188cb573dd713278b417d8fab9ea1bdc0ec6f9cb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 25 Sep 2024 13:27:13 +0300 Subject: [PATCH 001/175] add dummy vouchers list --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 32 ++++++++++++++++++++++++ services/registration/main.vis | 2 +- services/registration/select_voucher | 2 ++ services/registration/select_voucher.vis | 9 +++++++ 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 services/registration/select_voucher create mode 100644 services/registration/select_voucher.vis diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 1d6f5fd..b733efe 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -94,6 +94,7 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) + ls.DbRs.AddLocalFunc("get_vouchers",ussdHandlers.GetVoucherList) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ff8d8bd..7902cd2 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -254,6 +254,38 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt return res, nil } + +func (h *Handlers) GetVoucherList(ctx context.Context,sym string,input []byte) (resource.Result,error){ + var res resource.Result + vouchers := []string{ + "SRF", + "CRF", + "VCF", + "VSAPA", + "FSTMP", + "FSAW", + "PTAQ", + "VCRXT", + "VSGAQ", + "QPWIQQ", + "FSTMP", + "FSAW", + "PTAQ", + "VCRXT", + "VSGAQ", + "QPWIQQ", + "FSTMP", + "FSAW", + "PTAQ", + "VCRXT", + "VSGAQ", + "QPWIQQ", + } + res.Content = strings.Join(vouchers,"\n") + + return res,nil +} + func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) diff --git a/services/registration/main.vis b/services/registration/main.vis index d883dca..b5d791d 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -8,7 +8,7 @@ MOUT help 4 MOUT quit 9 HALT INCMP send 1 -INCMP quit 2 +INCMP select_voucher 2 INCMP my_account 3 INCMP help 4 INCMP quit 9 diff --git a/services/registration/select_voucher b/services/registration/select_voucher new file mode 100644 index 0000000..084b9b8 --- /dev/null +++ b/services/registration/select_voucher @@ -0,0 +1,2 @@ +Select number or symbol from your vouchers: +{{.get_vouchers}} \ No newline at end of file diff --git a/services/registration/select_voucher.vis b/services/registration/select_voucher.vis new file mode 100644 index 0000000..3919f71 --- /dev/null +++ b/services/registration/select_voucher.vis @@ -0,0 +1,9 @@ +LOAD get_vouchers 0 +MAP get_vouchers +MNEXT next 11 +MPREV back 22 +HALT +INCMP > 11 +INCMP < 22 +INCMP _* + From 7aa44caea27c8e45f5ac2a02e20c123430ccf0fa Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 25 Sep 2024 15:57:23 +0300 Subject: [PATCH 002/175] add voucher nodes --- services/registration/main.vis | 2 +- services/registration/my_vouchers | 1 + services/registration/my_vouchers.vis | 9 +++++++++ services/registration/select_voucher_menu | 1 + services/registration/voucher_details_menu | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 services/registration/my_vouchers create mode 100644 services/registration/my_vouchers.vis create mode 100644 services/registration/select_voucher_menu create mode 100644 services/registration/voucher_details_menu diff --git a/services/registration/main.vis b/services/registration/main.vis index b5d791d..1009039 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -8,7 +8,7 @@ MOUT help 4 MOUT quit 9 HALT INCMP send 1 -INCMP select_voucher 2 +INCMP my_vouchers 2 INCMP my_account 3 INCMP help 4 INCMP quit 9 diff --git a/services/registration/my_vouchers b/services/registration/my_vouchers new file mode 100644 index 0000000..548de9c --- /dev/null +++ b/services/registration/my_vouchers @@ -0,0 +1 @@ +My vouchers \ No newline at end of file diff --git a/services/registration/my_vouchers.vis b/services/registration/my_vouchers.vis new file mode 100644 index 0000000..b70dbfa --- /dev/null +++ b/services/registration/my_vouchers.vis @@ -0,0 +1,9 @@ +MOUT select_voucher 1 +MOUT voucher_details 2 +MOUT back 0 +HALT +INCMP _ 0 +INCMP select_voucher 1 + + + diff --git a/services/registration/select_voucher_menu b/services/registration/select_voucher_menu new file mode 100644 index 0000000..8ee06df --- /dev/null +++ b/services/registration/select_voucher_menu @@ -0,0 +1 @@ +Select voucher \ No newline at end of file diff --git a/services/registration/voucher_details_menu b/services/registration/voucher_details_menu new file mode 100644 index 0000000..a588f23 --- /dev/null +++ b/services/registration/voucher_details_menu @@ -0,0 +1 @@ +Voucher details \ No newline at end of file From 0e376e0d9e0bc7164bff7e4314c8ffb74ce5306a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 25 Sep 2024 16:03:08 +0300 Subject: [PATCH 003/175] include back and quit --- services/registration/select_voucher.vis | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/services/registration/select_voucher.vis b/services/registration/select_voucher.vis index 3919f71..4fb1d40 100644 --- a/services/registration/select_voucher.vis +++ b/services/registration/select_voucher.vis @@ -1,9 +1,11 @@ LOAD get_vouchers 0 MAP get_vouchers +MOUT back 0 +MOUT quit 9 MNEXT next 11 -MPREV back 22 +MPREV prev 22 HALT +INCMP _ 0 +INCMP quit 9 INCMP > 11 INCMP < 22 -INCMP _* - From 221db4e998ce4b5bcda91e6f99ec3aec18f28fbb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 25 Sep 2024 16:06:06 +0300 Subject: [PATCH 004/175] return a numbered list of vouchers --- internal/handlers/ussd/menuhandler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 7902cd2..e998bd5 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -281,7 +281,12 @@ func (h *Handlers) GetVoucherList(ctx context.Context,sym string,input []byte) ( "VSGAQ", "QPWIQQ", } - res.Content = strings.Join(vouchers,"\n") + + var numberedVouchers []string + for i, voucher := range vouchers { + numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher)) + } + res.Content = strings.Join(numberedVouchers,"\n") return res,nil } From 4c339450810a8ee212b2fe464fac847f567f7ad8 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 27 Sep 2024 19:10:08 +0300 Subject: [PATCH 005/175] use latest go-vise update --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c4c5167..e3a441a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.22.6 require ( - git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb + git.defalsify.org/vise.git v0.1.0-rc.3.0.20240926120105-89b0529cf7ac github.com/alecthomas/assert/v2 v2.2.2 github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 diff --git a/go.sum b/go.sum index ed5636f..3f7a262 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihcwMjDKzvUFC6t2zGNb7MDW+l/ACGlSAN1N8Y= git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= +git.defalsify.org/vise.git v0.1.0-rc.3.0.20240926120105-89b0529cf7ac h1:D4KI22KWXT8S66sHIjWhTBX6SXRfnd7j8VErq3PPbok= +git.defalsify.org/vise.git v0.1.0-rc.3.0.20240926120105-89b0529cf7ac/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= From 3a46fda769941e196b0e62ad12a4924782de9dd1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 28 Sep 2024 12:26:37 +0300 Subject: [PATCH 006/175] use save_temporary_pin and updated tests --- internal/handlers/handlerservice.go | 7 +-- internal/handlers/ussd/menuhandler.go | 59 +++++++------------- internal/handlers/ussd/menuhandler_test.go | 51 +++-------------- services/registration/account_creation.vis | 2 +- services/registration/confirm_create_pin.vis | 4 +- services/registration/create_pin.vis | 4 +- 6 files changed, 38 insertions(+), 89 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 3dffe16..1a556cc 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -60,8 +60,8 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ussdHandlers = ussdHandlers.WithPersister(ls.Pe) ls.DbRs.AddLocalFunc("set_language", ussdHandlers.SetLanguage) ls.DbRs.AddLocalFunc("create_account", ussdHandlers.CreateAccount) - ls.DbRs.AddLocalFunc("save_pin", ussdHandlers.SavePin) - ls.DbRs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin) + ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin) + ls.DbRs.AddLocalFunc("verify_create_pin", ussdHandlers.VerifyCreatePin) ls.DbRs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier) ls.DbRs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus) ls.DbRs.AddLocalFunc("authorize_account", ussdHandlers.Authorize) @@ -89,11 +89,10 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob) ls.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob) ls.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction) - ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin) ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) - ls.DbRs.AddLocalFunc("get_vouchers",ussdHandlers.GetVoucherList) + ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 5ebdaa5..165a456 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -180,34 +180,6 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, nil } -// SavePin persists the user's PIN choice into the filesystem -func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var err error - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - - accountPIN := string(input) - // Validate that the PIN is a 4-digit number - if !isValidPIN(accountPIN) { - res.FlagSet = append(res.FlagSet, flag_incorrect_pin) - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_incorrect_pin) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(accountPIN)) - if err != nil { - return res, err - } - return res, nil -} - func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} _, ok := ctx.Value("SessionId").(string) @@ -226,6 +198,9 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) ( return res, nil } +// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_PIN +// during the account creation process +// and during the change PIN process func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -234,6 +209,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt if !ok { return res, fmt.Errorf("missing session") } + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") accountPIN := string(input) @@ -243,16 +219,19 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagSet = append(res.FlagSet, flag_incorrect_pin) return res, nil } + + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) + store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) if err != nil { return res, err } + return res, nil } - -func (h *Handlers) GetVoucherList(ctx context.Context,sym string,input []byte) (resource.Result,error){ +func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result vouchers := []string{ "SRF", @@ -283,9 +262,9 @@ func (h *Handlers) GetVoucherList(ctx context.Context,sym string,input []byte) ( for i, voucher := range vouchers { numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher)) } - res.Content = strings.Join(numberedVouchers,"\n") + res.Content = strings.Join(numberedVouchers, "\n") - return res,nil + return res, nil } func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) { @@ -313,10 +292,10 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt return res, nil } -// VerifyPin checks whether the confirmation PIN is similar to the account PIN -// If similar, it sets the USERFLAG_PIN_SET flag allowing the user +// VerifyCreatePin checks whether the confirmation PIN is similar to the temporary PIN +// If similar, it sets the USERFLAG_PIN_SET flag and writes the account PIN allowing the user // to access the main menu -func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { +func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") @@ -328,14 +307,13 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res return res, fmt.Errorf("missing session") } - //AccountPin, _ := utils.ReadEntry(ctx, h.userdataStore, sessionId, utils.DATA_ACCOUNT_PIN) store := h.userdataStore - AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) if err != nil { return res, err } - if bytes.Equal(input, AccountPin) { + if bytes.Equal(input, temporaryPin) { res.FlagSet = []uint32{flag_valid_pin} res.FlagReset = []uint32{flag_pin_mismatch} res.FlagSet = append(res.FlagSet, flag_pin_set) @@ -343,6 +321,11 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res res.FlagSet = []uint32{flag_pin_mismatch} } + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + if err != nil { + return res, err + } + return res, nil } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 83e6f0c..1ce8b51 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -171,7 +171,7 @@ func TestSaveFamilyname(t *testing.T) { mockStore.AssertExpectations(t) } -func TestSavePin(t *testing.T) { +func TestSaveTemporaryPIn(t *testing.T) { fm, err := NewFlagManager(flagsPath) mockStore := new(mocks.MockUserDataStore) if err != nil { @@ -213,10 +213,10 @@ func TestSavePin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.input)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) // Call the method - res, err := h.SavePin(ctx, "save_pin", tt.input) + res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) if err != nil { t.Error(err) @@ -937,7 +937,7 @@ func TestVerifyYob(t *testing.T) { } } -func TestVerifyPin(t *testing.T) { +func TestVerifyCreatePin(t *testing.T) { fm, err := NewFlagManager(flagsPath) if err != nil { @@ -986,7 +986,7 @@ func TestVerifyPin(t *testing.T) { }, } - typ := utils.DATA_ACCOUNT_PIN + typ := utils.DATA_TEMPORARY_PIN for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -994,8 +994,11 @@ func TestVerifyPin(t *testing.T) { // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte(firstSetPin), nil) + // Set up the expected behavior of the mock + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(firstSetPin)).Return(nil) + // Call the method under test - res, err := h.VerifyPin(ctx, "verify_pin", []byte(tt.input)) + res, err := h.VerifyCreatePin(ctx, "verify_create_pin", []byte(tt.input)) // Assert that no errors occurred assert.NoError(t, err) @@ -1693,42 +1696,6 @@ func TestVerifyNewPin(t *testing.T) { } -func TestSaveTemporaryPIn(t *testing.T) { - - fm, err := NewFlagManager(flagsPath) - - if err != nil { - t.Logf(err.Error()) - } - - // Create a new instance of UserDataStore - mockStore := new(mocks.MockUserDataStore) - - // Define test data - sessionId := "session123" - PIN := "1234" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(PIN)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveTemporaryPin(ctx, "save_temporary_pin", []byte(PIN)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(t) -} - func TestConfirmPin(t *testing.T) { sessionId := "session123" diff --git a/services/registration/account_creation.vis b/services/registration/account_creation.vis index f4f326b..380fe6d 100644 --- a/services/registration/account_creation.vis +++ b/services/registration/account_creation.vis @@ -1,4 +1,4 @@ -RELOAD verify_pin +RELOAD verify_create_pin CATCH create_pin_mismatch flag_pin_mismatch 1 LOAD quit 0 HALT diff --git a/services/registration/confirm_create_pin.vis b/services/registration/confirm_create_pin.vis index 1235916..1a3173c 100644 --- a/services/registration/confirm_create_pin.vis +++ b/services/registration/confirm_create_pin.vis @@ -1,4 +1,4 @@ -LOAD save_pin 0 +LOAD save_temporary_pin 0 HALT -LOAD verify_pin 8 +LOAD verify_create_pin 8 INCMP account_creation * diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index e0e330f..c76e1bf 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -2,8 +2,8 @@ LOAD create_account 0 CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT -LOAD save_pin 0 -RELOAD save_pin +LOAD save_temporary_pin 0 +RELOAD save_temporary_pin CATCH . flag_incorrect_pin 1 INCMP quit 0 INCMP confirm_create_pin * From 31aea6b807ed352b44e7640a794bab87269dbe4e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 28 Sep 2024 14:07:37 +0300 Subject: [PATCH 007/175] added model and func to fetch vouchers from the API --- internal/handlers/server/accountservice.go | 29 ++++++++++++++++++ internal/handlers/ussd/menuhandler.go | 35 +++++++--------------- internal/mocks/servicemock.go | 7 ++++- internal/models/vouchersresponse.go | 7 +++++ 4 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 internal/models/vouchersresponse.go diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index f4375a1..ca460c7 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -13,6 +13,7 @@ type AccountServiceInterface interface { CheckBalance(publicKey string) (string, error) CreateAccount() (*models.AccountResponse, error) CheckAccountStatus(trackingId string) (string, error) + FetchVouchersFromAPI() ([]models.VoucherHolding, error) } type AccountService struct { @@ -110,3 +111,31 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return &accountResp, nil } + +// fetchVouchersFromAPI calls the API to get the list of vouchers belonging to the user +func (as *AccountService) FetchVouchersFromAPI() ([]models.VoucherHolding, error) { + // TODO replace with the actual request once ready + mockJSON := `[ + { + "symbol": "MUMO", + "address": "0x078b3a26596218507781722A4e8825BFB9570Fba" + }, + { + "symbol": "SRF", + "address": "0x45d747172e77d55575c197CbA9451bC2CD8F4958" + }, + { + "symbol": "HALGAN", + "address": "0x12169Fb5931A599ad1283bb8311Dad54Feb51A28" + } + ]` + + // Unmarshal the JSON response + var holdings []models.VoucherHolding + err := json.Unmarshal([]byte(mockJSON), &holdings) + if err != nil { + return nil, err + } + + return holdings, nil +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 165a456..19c7d1a 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -231,36 +231,23 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt return res, nil } +// GetVoucherList fetches the list of vouchers and formats them +// checks whether they are synced internally before calling the API func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - vouchers := []string{ - "SRF", - "CRF", - "VCF", - "VSAPA", - "FSTMP", - "FSAW", - "PTAQ", - "VCRXT", - "VSGAQ", - "QPWIQQ", - "FSTMP", - "FSAW", - "PTAQ", - "VCRXT", - "VSGAQ", - "QPWIQQ", - "FSTMP", - "FSAW", - "PTAQ", - "VCRXT", - "VSGAQ", - "QPWIQQ", + + // check if the vouchers exist internally and if not + // fetch from the API + + // Fetch vouchers from API + vouchers, err := h.accountService.FetchVouchersFromAPI() + if err != nil { + return res, fmt.Errorf("error fetching vouchers: %w", err) } var numberedVouchers []string for i, voucher := range vouchers { - numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher)) + numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher.Symbol)) } res.Content = strings.Join(numberedVouchers, "\n") diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 9fb6d3e..30386b3 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -23,4 +23,9 @@ func (m *MockAccountService) CheckBalance(publicKey string) (string, error) { func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, error) { args := m.Called(trackingId) return args.String(0), args.Error(1) -} \ No newline at end of file +} + +func (m *MockAccountService) FetchVouchersFromAPI() ([]models.VoucherHolding, error) { + args := m.Called() + return args.Get(0).([]models.VoucherHolding), args.Error(1) +} diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go new file mode 100644 index 0000000..08967b7 --- /dev/null +++ b/internal/models/vouchersresponse.go @@ -0,0 +1,7 @@ +package models + +// VoucherHolding represents a single voucher holding +type VoucherHolding struct { + Symbol string `json:"symbol"` + Address string `json:"address"` +} \ No newline at end of file From 517f9806643b4012cf6aaedfbe3450f4c781c0c6 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 5 Oct 2024 16:56:49 +0300 Subject: [PATCH 008/175] get the vouchers list and store in gdbm --- internal/handlers/handlerservice.go | 1 + internal/handlers/server/accountservice.go | 100 +++++++++++++++------ internal/handlers/ussd/menuhandler.go | 63 +++++++++++-- internal/mocks/servicemock.go | 4 +- internal/models/vouchersresponse.go | 18 ++-- internal/utils/db.go | 1 + services/registration/main.vis | 2 + 7 files changed, 152 insertions(+), 37 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 1a556cc..8f0ff16 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -92,6 +92,7 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) + ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers) ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) return ussdHandlers, nil diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index ca460c7..d53db57 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -13,14 +13,12 @@ type AccountServiceInterface interface { CheckBalance(publicKey string) (string, error) CreateAccount() (*models.AccountResponse, error) CheckAccountStatus(trackingId string) (string, error) - FetchVouchersFromAPI() ([]models.VoucherHolding, error) + FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) } type AccountService struct { } - - // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. // // Parameters: @@ -28,12 +26,10 @@ type AccountService struct { // CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the // AccountResponse struct can be used here to check the account status during a transaction. // -// // Returns: // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -// func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) { resp, err := http.Get(config.TrackStatusURL + trackingId) if err != nil { @@ -57,7 +53,6 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) return status, nil } - // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. @@ -84,8 +79,7 @@ func (as *AccountService) CheckBalance(publicKey string) (string, error) { return balance, nil } - -//CreateAccount creates a new account in the custodial system. +// CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. // If there is an error during the request or processing, this will be nil. @@ -112,30 +106,86 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return &accountResp, nil } -// fetchVouchersFromAPI calls the API to get the list of vouchers belonging to the user -func (as *AccountService) FetchVouchersFromAPI() ([]models.VoucherHolding, error) { +// FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint +// Parameters: +// - publicKey: The public key associated with the account. +func (as *AccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { // TODO replace with the actual request once ready - mockJSON := `[ - { - "symbol": "MUMO", - "address": "0x078b3a26596218507781722A4e8825BFB9570Fba" - }, - { - "symbol": "SRF", - "address": "0x45d747172e77d55575c197CbA9451bC2CD8F4958" - }, - { - "symbol": "HALGAN", - "address": "0x12169Fb5931A599ad1283bb8311Dad54Feb51A28" + mockJSON := `{ + "ok": true, + "description": "Token holdings with current balances", + "result": { + "holdings": [ + { + "contractAddress": "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", + "tokenSymbol": "FSPTST", + "tokenDecimals": "6", + "balance": "8869964242" + }, + { + "contractAddress": "0x724F2910D790B54A39a7638282a45B1D83564fFA", + "tokenSymbol": "GEO", + "tokenDecimals": "6", + "balance": "9884" + }, + { + "contractAddress": "0x2105a206B7bec31E2F90acF7385cc8F7F5f9D273", + "tokenSymbol": "MFNK", + "tokenDecimals": "6", + "balance": "19788697" + }, + { + "contractAddress": "0x63DE2Ac8D1008351Cc69Fb8aCb94Ba47728a7E83", + "tokenSymbol": "MILO", + "tokenDecimals": "6", + "balance": "75" + }, + { + "contractAddress": "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + "tokenSymbol": "SOHAIL", + "tokenDecimals": "6", + "balance": "27874115" + }, + { + "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", + "tokenSymbol": "SRQIF", + "tokenDecimals": "6", + "balance": "2745987" + }, + { + "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", + "tokenSymbol": "SRFI", + "tokenDecimals": "6", + "balance": "2745987" + }, + { + "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", + "tokenSymbol": "SRFU", + "tokenDecimals": "6", + "balance": "2745987" + }, + { + "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", + "tokenSymbol": "SRQF", + "tokenDecimals": "6", + "balance": "2745987" + }, + { + "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", + "tokenSymbol": "SREF", + "tokenDecimals": "6", + "balance": "2745987" + } + ] } - ]` + }` // Unmarshal the JSON response - var holdings []models.VoucherHolding + var holdings models.VoucherHoldingResponse err := json.Unmarshal([]byte(mockJSON), &holdings) if err != nil { return nil, err } - return holdings, nil + return &holdings, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 19c7d1a..d55abaf 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -3,6 +3,7 @@ package ussd import ( "bytes" "context" + "encoding/json" "fmt" "path" "regexp" @@ -232,22 +233,36 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt } // GetVoucherList fetches the list of vouchers and formats them -// checks whether they are synced internally before calling the API +// checks whether they are stored internally before calling the API func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } // check if the vouchers exist internally and if not // fetch from the API - // Fetch vouchers from API - vouchers, err := h.accountService.FetchVouchersFromAPI() + // Read vouchers from the store + store := h.userdataStore + voucherData, err := store.ReadEntry(ctx, sessionId, utils.DATA_VOUCHER_LIST) if err != nil { - return res, fmt.Errorf("error fetching vouchers: %w", err) + return res, err + } + + // Unmarshal the stored JSON data into the correct struct + var vouchers []struct { + TokenSymbol string `json:"tokenSymbol"` + } + err = json.Unmarshal(voucherData, &vouchers) + if err != nil { + return res, fmt.Errorf("failed to unmarshal vouchers: %v", err) } var numberedVouchers []string for i, voucher := range vouchers { - numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher.Symbol)) + numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) } res.Content = strings.Join(numberedVouchers, "\n") @@ -1007,3 +1022,41 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) return res, nil } + +// CheckVouchers retrieves the token holdings from the API using the "PublicKey" and stores +// them to gdbm +func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + if err != nil { + return res, nil + } + + // Fetch vouchers from the API + vouchersResp, err := h.accountService.FetchVouchers(string(publicKey)) + if err != nil { + return res, nil + } + + // Convert only the list of holdings (vouchers) to JSON + voucherBytes, err := json.Marshal(vouchersResp.Result.Holdings) + if err != nil { + return res, nil + } + + // Store the voucher symbols in the userdataStore + err = store.WriteEntry(ctx, sessionId, utils.DATA_VOUCHER_LIST, voucherBytes) + if err != nil { + return res, nil + } + + return res, nil +} diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 30386b3..8fbde0f 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -25,7 +25,7 @@ func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, erro return args.String(0), args.Error(1) } -func (m *MockAccountService) FetchVouchersFromAPI() ([]models.VoucherHolding, error) { +func (m *MockAccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { args := m.Called() - return args.Get(0).([]models.VoucherHolding), args.Error(1) + return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) } diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go index 08967b7..010730f 100644 --- a/internal/models/vouchersresponse.go +++ b/internal/models/vouchersresponse.go @@ -1,7 +1,15 @@ package models -// VoucherHolding represents a single voucher holding -type VoucherHolding struct { - Symbol string `json:"symbol"` - Address string `json:"address"` -} \ No newline at end of file +// VoucherHoldingResponse represents a single voucher holding +type VoucherHoldingResponse struct { + Ok bool `json:"ok"` + Description string `json:"description"` + Result struct { + Holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + } `json:"holdings"` + } `json:"result"` +} diff --git a/internal/utils/db.go b/internal/utils/db.go index 410da68..3080c1b 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -23,6 +23,7 @@ const ( DATA_RECIPIENT DATA_AMOUNT DATA_TEMPORARY_PIN + DATA_VOUCHER_LIST ) func typToBytes(typ DataTyp) []byte { diff --git a/services/registration/main.vis b/services/registration/main.vis index 1009039..88f8a42 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -1,5 +1,7 @@ LOAD check_balance 64 RELOAD check_balance +LOAD check_vouchers 10 +RELOAD check_vouchers MAP check_balance MOUT send 1 MOUT vouchers 2 From 755899be4e6a1e0dc5c6b731910ade18d40284ad Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 7 Oct 2024 08:59:15 +0300 Subject: [PATCH 009/175] store the pre-rendered vouchers list --- internal/handlers/ussd/menuhandler.go | 32 +++++++-------------------- internal/storage/db.go | 28 +++++++++++------------ 2 files changed, 22 insertions(+), 38 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d55abaf..4c09acb 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -3,7 +3,6 @@ package ussd import ( "bytes" "context" - "encoding/json" "fmt" "path" "regexp" @@ -251,20 +250,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) return res, err } - // Unmarshal the stored JSON data into the correct struct - var vouchers []struct { - TokenSymbol string `json:"tokenSymbol"` - } - err = json.Unmarshal(voucherData, &vouchers) - if err != nil { - return res, fmt.Errorf("failed to unmarshal vouchers: %v", err) - } - - var numberedVouchers []string - for i, voucher := range vouchers { - numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) - } - res.Content = strings.Join(numberedVouchers, "\n") + res.Content = string(voucherData) return res, nil } @@ -1027,8 +1013,6 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) // them to gdbm func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error - sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") @@ -1040,20 +1024,20 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - // Fetch vouchers from the API + // Fetch vouchers from the API using the public key vouchersResp, err := h.accountService.FetchVouchers(string(publicKey)) if err != nil { return res, nil } - // Convert only the list of holdings (vouchers) to JSON - voucherBytes, err := json.Marshal(vouchersResp.Result.Holdings) - if err != nil { - return res, nil + var numberedVouchers []string + for i, voucher := range vouchersResp.Result.Holdings { + numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) } - // Store the voucher symbols in the userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_VOUCHER_LIST, voucherBytes) + voucherList := strings.Join(numberedVouchers, "\n") + + err = store.WriteEntry(ctx, sessionId, utils.DATA_VOUCHER_LIST, []byte(voucherList)) if err != nil { return res, nil } diff --git a/internal/storage/db.go b/internal/storage/db.go index b2ac6a9..060f0c0 100644 --- a/internal/storage/db.go +++ b/internal/storage/db.go @@ -12,32 +12,32 @@ const ( type SubPrefixDb struct { store db.Db - pfx []byte + pfx []byte } func NewSubPrefixDb(store db.Db, pfx []byte) *SubPrefixDb { return &SubPrefixDb{ store: store, - pfx: pfx, + pfx: pfx, } } -func(s *SubPrefixDb) toKey(k []byte) []byte { - return append(s.pfx, k...) +func (s *SubPrefixDb) toKey(k []byte) []byte { + return append(s.pfx, k...) } -func(s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { - s.store.SetPrefix(DATATYPE_USERSUB) +func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { + s.store.SetPrefix(DATATYPE_USERSUB) key = s.toKey(key) - v, err := s.store.Get(ctx, key) - if err != nil { - return nil, err - } - return v, nil + v, err := s.store.Get(ctx, key) + if err != nil { + return nil, err + } + return v, nil } -func(s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error { - s.store.SetPrefix(DATATYPE_USERSUB) +func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error { + s.store.SetPrefix(DATATYPE_USERSUB) key = s.toKey(key) - return s.store.Put(ctx, key, val) + return s.store.Put(ctx, key, val) } From e4ed9a65bbc777bf98232a0b887fab73530ae291 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 7 Oct 2024 13:49:12 +0300 Subject: [PATCH 010/175] store the symbols and balances --- internal/handlers/server/accountservice.go | 36 ---------------------- internal/handlers/ussd/menuhandler.go | 29 +++++++++++------ 2 files changed, 19 insertions(+), 46 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index d53db57..9f6ce03 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -139,42 +139,6 @@ func (as *AccountService) FetchVouchers(publicKey string) (*models.VoucherHoldin "tokenSymbol": "MILO", "tokenDecimals": "6", "balance": "75" - }, - { - "contractAddress": "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", - "tokenSymbol": "SOHAIL", - "tokenDecimals": "6", - "balance": "27874115" - }, - { - "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", - "tokenSymbol": "SRQIF", - "tokenDecimals": "6", - "balance": "2745987" - }, - { - "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", - "tokenSymbol": "SRFI", - "tokenDecimals": "6", - "balance": "2745987" - }, - { - "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", - "tokenSymbol": "SRFU", - "tokenDecimals": "6", - "balance": "2745987" - }, - { - "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", - "tokenSymbol": "SRQF", - "tokenDecimals": "6", - "balance": "2745987" - }, - { - "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", - "tokenSymbol": "SREF", - "tokenDecimals": "6", - "balance": "2745987" } ] } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4c09acb..0ff1017 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -21,6 +21,8 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" "gopkg.in/leonelquinteros/gotext.v1" + + "git.grassecon.net/urdt/ussd/internal/storage" ) var ( @@ -235,19 +237,17 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt // checks whether they are stored internally before calling the API func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } // check if the vouchers exist internally and if not // fetch from the API // Read vouchers from the store store := h.userdataStore - voucherData, err := store.ReadEntry(ctx, sessionId, utils.DATA_VOUCHER_LIST) + prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) + + voucherData, err := prefixdb.Get(ctx, []byte("tokens")) if err != nil { - return res, err + return res, nil } res.Content = string(voucherData) @@ -1030,14 +1030,23 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - var numberedVouchers []string + var numberedSymbols []string + var numberedBalances []string for i, voucher := range vouchersResp.Result.Holdings { - numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) + numberedSymbols = append(numberedSymbols, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) + numberedBalances = append(numberedBalances, fmt.Sprintf("%d:%s", i+1, voucher.Balance)) } - voucherList := strings.Join(numberedVouchers, "\n") + voucherSymbolList := strings.Join(numberedSymbols, "\n") + voucherBalanceList := strings.Join(numberedBalances, "\n") - err = store.WriteEntry(ctx, sessionId, utils.DATA_VOUCHER_LIST, []byte(voucherList)) + prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) + err = prefixdb.Put(ctx, []byte("tokens"), []byte(voucherSymbolList)) + if err != nil { + return res, nil + } + + err = prefixdb.Put(ctx, []byte(voucherSymbolList), []byte(voucherBalanceList)) if err != nil { return res, nil } From 06ebcc0f07886f05ece7c0c04e3a71ad6312c5e5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 7 Oct 2024 16:15:13 +0300 Subject: [PATCH 011/175] added swahili template --- services/registration/select_voucher_swa | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 services/registration/select_voucher_swa diff --git a/services/registration/select_voucher_swa b/services/registration/select_voucher_swa new file mode 100644 index 0000000..b4720bf --- /dev/null +++ b/services/registration/select_voucher_swa @@ -0,0 +1,2 @@ +Chagua nambari au ishara kutoka kwa salio zako: +{{.get_vouchers}} \ No newline at end of file From cb4a52e4f245bddf9755434004a4cdb7fffd9f65 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 7 Oct 2024 16:16:57 +0300 Subject: [PATCH 012/175] view a selected voucher and verify the PIN --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 68 ++++++++++++++++++++++++ services/registration/my_vouchers.vis | 3 -- services/registration/pp.csv | 2 +- services/registration/select_voucher.vis | 6 ++- services/registration/view_voucher | 2 + services/registration/view_voucher.vis | 11 ++++ services/registration/view_voucher_swa | 2 + services/registration/voucher_set | 1 + services/registration/voucher_set.vis | 5 ++ services/registration/voucher_set_swa | 1 + 11 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 services/registration/view_voucher create mode 100644 services/registration/view_voucher.vis create mode 100644 services/registration/view_voucher_swa create mode 100644 services/registration/voucher_set create mode 100644 services/registration/voucher_set.vis create mode 100644 services/registration/voucher_set_swa diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 8f0ff16..4c152b9 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -94,6 +94,7 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers) ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) + ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 0ff1017..c254401 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1053,3 +1053,71 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } + +// ViewVoucher retrieves the token holding and balance from the subprefixDB +func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + inputStr := string(input) + + if inputStr == "0" || inputStr == "00" { + return res, nil + } + + flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") + + // Initialize the store and prefix database + store := h.userdataStore + prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) + + // Retrieve the voucher symbol list + voucherSymbolList, err := prefixdb.Get(ctx, []byte("tokens")) + if err != nil { + return res, fmt.Errorf("failed to retrieve voucher symbol list: %v", err) + } + + // Retrieve the voucher balance list + voucherBalanceList, err := prefixdb.Get(ctx, []byte(voucherSymbolList)) + if err != nil { + return res, fmt.Errorf("failed to retrieve voucher balance list: %v", err) + } + + // Convert the symbol and balance lists from byte arrays to strings + voucherSymbols := string(voucherSymbolList) + voucherBalances := string(voucherBalanceList) + + // Split the lists into slices for processing + symbols := strings.Split(voucherSymbols, "\n") + balances := strings.Split(voucherBalances, "\n") + + var matchedSymbol, matchedBalance string + + for i, symbol := range symbols { + symbolParts := strings.SplitN(symbol, ":", 2) + if len(symbolParts) != 2 { + continue + } + voucherNum := symbolParts[0] + voucherSymbol := symbolParts[1] + + // Check if input matches either the number or the symbol + if inputStr == voucherNum || strings.EqualFold(inputStr, voucherSymbol) { + matchedSymbol = voucherSymbol + // Ensure there's a corresponding balance + if i < len(balances) { + matchedBalance = strings.SplitN(balances[i], ":", 2)[1] // Extract balance after the "x:balance" format + } + break + } + } + + // If a match is found, return the symbol and balance + if matchedSymbol != "" && matchedBalance != "" { + res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) + res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) + } else { + res.FlagSet = append(res.FlagSet, flag_incorrect_voucher) + } + + return res, nil +} diff --git a/services/registration/my_vouchers.vis b/services/registration/my_vouchers.vis index b70dbfa..9702573 100644 --- a/services/registration/my_vouchers.vis +++ b/services/registration/my_vouchers.vis @@ -4,6 +4,3 @@ MOUT back 0 HALT INCMP _ 0 INCMP select_voucher 1 - - - diff --git a/services/registration/pp.csv b/services/registration/pp.csv index fd552e4..2a7bb21 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -12,5 +12,5 @@ flag,flag_invalid_amount,18,this is set when the given transaction amount is inv flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN flag,flag_valid_pin,20,this is set when the given PIN is valid flag,flag_allow_update,21,this is set to allow a user to update their profile data -flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth +flag,flag_incorrect_voucher,22,this is set when the selected voucher is invalid flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid diff --git a/services/registration/select_voucher.vis b/services/registration/select_voucher.vis index 4fb1d40..50b99ad 100644 --- a/services/registration/select_voucher.vis +++ b/services/registration/select_voucher.vis @@ -1,11 +1,15 @@ LOAD get_vouchers 0 MAP get_vouchers MOUT back 0 -MOUT quit 9 +MOUT quit 00 MNEXT next 11 MPREV prev 22 HALT +LOAD view_voucher 80 +RELOAD view_voucher +CATCH . flag_incorrect_voucher 1 INCMP _ 0 INCMP quit 9 INCMP > 11 INCMP < 22 +INCMP view_voucher * diff --git a/services/registration/view_voucher b/services/registration/view_voucher new file mode 100644 index 0000000..3940982 --- /dev/null +++ b/services/registration/view_voucher @@ -0,0 +1,2 @@ +Enter PIN to confirm selection: +{{.view_voucher}} \ No newline at end of file diff --git a/services/registration/view_voucher.vis b/services/registration/view_voucher.vis new file mode 100644 index 0000000..ee8bf85 --- /dev/null +++ b/services/registration/view_voucher.vis @@ -0,0 +1,11 @@ +RELOAD view_voucher +MAP view_voucher +MOUT back 0 +MOUT quit 9 +LOAD authorize_account 6 +HALT +RELOAD authorize_account +CATCH incorrect_pin flag_incorrect_pin 1 +INCMP _ 0 +INCMP quit 9 +INCMP voucher_set * diff --git a/services/registration/view_voucher_swa b/services/registration/view_voucher_swa new file mode 100644 index 0000000..485e2ef --- /dev/null +++ b/services/registration/view_voucher_swa @@ -0,0 +1,2 @@ +Weka PIN ili kuthibitisha chaguo: +{{.view_voucher}} \ No newline at end of file diff --git a/services/registration/voucher_set b/services/registration/voucher_set new file mode 100644 index 0000000..838c1ef --- /dev/null +++ b/services/registration/voucher_set @@ -0,0 +1 @@ +Success! symbol is now your active voucher. \ No newline at end of file diff --git a/services/registration/voucher_set.vis b/services/registration/voucher_set.vis new file mode 100644 index 0000000..832ef22 --- /dev/null +++ b/services/registration/voucher_set.vis @@ -0,0 +1,5 @@ +MOUT back 0 +MOUT quit 9 +HALT +INCMP ^ 0 +INCMP quit 9 diff --git a/services/registration/voucher_set_swa b/services/registration/voucher_set_swa new file mode 100644 index 0000000..320a315 --- /dev/null +++ b/services/registration/voucher_set_swa @@ -0,0 +1 @@ +Hongera! symbol ni Sarafu inayotumika sasa. \ No newline at end of file From 7b374ad801a37e1b88f2c45ce3b7d92852a68880 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 8 Oct 2024 13:41:09 +0300 Subject: [PATCH 013/175] define importable token list --- internal/handlers/server/accountservice.go | 22 ++++--- internal/handlers/ussd/menuhandler.go | 68 +++++++++++++--------- sample_tokens.json | 44 ++++++++++++++ 3 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 sample_tokens.json diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index f4375a1..88c3354 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io" "net/http" + "os" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" @@ -18,8 +19,6 @@ type AccountServiceInterface interface { type AccountService struct { } - - // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. // // Parameters: @@ -27,12 +26,10 @@ type AccountService struct { // CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the // AccountResponse struct can be used here to check the account status during a transaction. // -// // Returns: // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -// func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) { resp, err := http.Get(config.TrackStatusURL + trackingId) if err != nil { @@ -56,7 +53,6 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) return status, nil } - // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. @@ -83,8 +79,7 @@ func (as *AccountService) CheckBalance(publicKey string) (string, error) { return balance, nil } - -//CreateAccount creates a new account in the custodial system. +// CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. // If there is an error during the request or processing, this will be nil. @@ -110,3 +105,16 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return &accountResp, nil } + +func GetTokenList() (*models.ApiResponse, error) { + file, err := os.Open("sample_tokens.json") + if err != nil { + return nil, err + } + defer file.Close() + var apiResponse models.ApiResponse + if err := json.NewDecoder(file).Decode(&apiResponse); err != nil { + return nil, err + } + return &apiResponse, nil +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index e998bd5..a7a58bf 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -256,36 +256,50 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt func (h *Handlers) GetVoucherList(ctx context.Context,sym string,input []byte) (resource.Result,error){ - var res resource.Result - vouchers := []string{ - "SRF", - "CRF", - "VCF", - "VSAPA", - "FSTMP", - "FSAW", - "PTAQ", - "VCRXT", - "VSGAQ", - "QPWIQQ", - "FSTMP", - "FSAW", - "PTAQ", - "VCRXT", - "VSGAQ", - "QPWIQQ", - "FSTMP", - "FSAW", - "PTAQ", - "VCRXT", - "VSGAQ", - "QPWIQQ", + res := resource.Result{} + //as := h.accountService.(*server.AccountService) + tokenList,err := server.GetTokenList() + fmt.Println("Error here:",err) + if err != nil { + return res,err } - var numberedVouchers []string - for i, voucher := range vouchers { - numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher)) + holdings := tokenList.Result.Holdings + fmt.Println("TokenList:",tokenList.Result.Holdings) + + // vouchers := []string{ + // "SRF", + // "CRF", + // "VCF", + // "VSAPA", + // "FSTMP", + // "FSAW", + // "PTAQ", + // "VCRXT", + // "VSGAQ", + // "QPWIQQ", + // "FSTMP", + // "FSAW", + // "PTAQ", + // "VCRXT", + // "VSGAQ", + // "QPWIQQ", + // "FSTMP", + // "FSAW", + // "PTAQ", + // "VCRXT", + // "VSGAQ", + // "QPWIQQ", + // } + var numberedVouchers []string + for i,token := range holdings { + numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, token.TokenSymbol)) } + + // var numberedVouchers []string + // for i, voucher := range vouchers { + // numberedVouchers = append(numberedVouchers, fmt.Sprintf("%d:%s", i+1, voucher)) + // } res.Content = strings.Join(numberedVouchers,"\n") return res,nil diff --git a/sample_tokens.json b/sample_tokens.json new file mode 100644 index 0000000..07126ed --- /dev/null +++ b/sample_tokens.json @@ -0,0 +1,44 @@ +{ + "ok": true, + "description": "Token holdings with current balances", + "result": { + "holdings": [ + { + "contractAddress": "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", + "tokenSymbol": "FSPTST", + "tokenDecimals": "6", + "balance": "8869964242" + }, + { + "contractAddress": "0x724F2910D790B54A39a7638282a45B1D83564fFA", + "tokenSymbol": "GEO", + "tokenDecimals": "6", + "balance": "9884" + }, + { + "contractAddress": "0x2105a206B7bec31E2F90acF7385cc8F7F5f9D273", + "tokenSymbol": "MFNK", + "tokenDecimals": "6", + "balance": "19788697" + }, + { + "contractAddress": "0x63DE2Ac8D1008351Cc69Fb8aCb94Ba47728a7E83", + "tokenSymbol": "MILO", + "tokenDecimals": "6", + "balance": "75" + }, + { + "contractAddress": "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + "tokenSymbol": "SOHAIL", + "tokenDecimals": "6", + "balance": "27874115" + }, + { + "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958", + "tokenSymbol": "SRF", + "tokenDecimals": "6", + "balance": "2745987" + } + ] + } + } From 4b6fd35e7a3c66ea3f28f03b6abd212b38bd0007 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 8 Oct 2024 13:43:57 +0300 Subject: [PATCH 014/175] define importable voucher response --- internal/models/tokenresponse.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 internal/models/tokenresponse.go diff --git a/internal/models/tokenresponse.go b/internal/models/tokenresponse.go new file mode 100644 index 0000000..d243d93 --- /dev/null +++ b/internal/models/tokenresponse.go @@ -0,0 +1,18 @@ +package models + +type ApiResponse struct { + OK bool `json:"ok"` + Description string `json:"description"` + Result Result `json:"result"` +} + +type Result struct { + Holdings []Holding `json:"holdings"` +} + +type Holding struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` +} From 2c98a8e1331861a0c1754ed62ea72baec1853af3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 8 Oct 2024 14:34:21 +0300 Subject: [PATCH 015/175] read token list from json file --- internal/handlers/server/accountservice.go | 53 ++-------------------- 1 file changed, 5 insertions(+), 48 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 2b0efc0..b83507f 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -111,59 +111,16 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { // Parameters: // - publicKey: The public key associated with the account. func (as *AccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { - // TODO replace with the actual request once ready - mockJSON := `{ - "ok": true, - "description": "Token holdings with current balances", - "result": { - "holdings": [ - { - "contractAddress": "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", - "tokenSymbol": "FSPTST", - "tokenDecimals": "6", - "balance": "8869964242" - }, - { - "contractAddress": "0x724F2910D790B54A39a7638282a45B1D83564fFA", - "tokenSymbol": "GEO", - "tokenDecimals": "6", - "balance": "9884" - }, - { - "contractAddress": "0x2105a206B7bec31E2F90acF7385cc8F7F5f9D273", - "tokenSymbol": "MFNK", - "tokenDecimals": "6", - "balance": "19788697" - }, - { - "contractAddress": "0x63DE2Ac8D1008351Cc69Fb8aCb94Ba47728a7E83", - "tokenSymbol": "MILO", - "tokenDecimals": "6", - "balance": "75" - } - ] - } - }` - - // Unmarshal the JSON response - var holdings models.VoucherHoldingResponse - err := json.Unmarshal([]byte(mockJSON), &holdings) - if err != nil { - return nil, err - } - - return &holdings, nil -} - -func GetTokenList() (*models.ApiResponse, error) { file, err := os.Open("sample_tokens.json") if err != nil { return nil, err } defer file.Close() - var apiResponse models.ApiResponse - if err := json.NewDecoder(file).Decode(&apiResponse); err != nil { + var holdings models.VoucherHoldingResponse + + if err := json.NewDecoder(file).Decode(&holdings); err != nil { return nil, err } - return &apiResponse, nil + return &holdings, nil } + From 9ccb6cc066cbbb1f0da585d6d1f6f0941901b6e3 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 9 Oct 2024 15:05:59 +0300 Subject: [PATCH 016/175] use 99 as the quit --- internal/handlers/ussd/menuhandler.go | 2 +- services/registration/select_voucher.vis | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index c254401..b634f91 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1060,7 +1060,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r var err error inputStr := string(input) - if inputStr == "0" || inputStr == "00" { + if inputStr == "0" || inputStr == "99" { return res, nil } diff --git a/services/registration/select_voucher.vis b/services/registration/select_voucher.vis index 50b99ad..08aa434 100644 --- a/services/registration/select_voucher.vis +++ b/services/registration/select_voucher.vis @@ -1,7 +1,7 @@ LOAD get_vouchers 0 MAP get_vouchers MOUT back 0 -MOUT quit 00 +MOUT quit 99 MNEXT next 11 MPREV prev 22 HALT @@ -9,7 +9,7 @@ LOAD view_voucher 80 RELOAD view_voucher CATCH . flag_incorrect_voucher 1 INCMP _ 0 -INCMP quit 9 +INCMP quit 99 INCMP > 11 INCMP < 22 INCMP view_voucher * From 6c904a8b3fab516bb49d1a950cda05785883fc0a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 9 Oct 2024 15:38:19 +0300 Subject: [PATCH 017/175] add the temporary and active symbol --- internal/utils/db.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/utils/db.go b/internal/utils/db.go index 3080c1b..f70e08b 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -24,6 +24,8 @@ const ( DATA_AMOUNT DATA_TEMPORARY_PIN DATA_VOUCHER_LIST + DATA_TEMPORARY_SYM + DATA_ACTIVE_SYM ) func typToBytes(typ DataTyp) []byte { From 9ad7d5a5226619eac07e99bcd4ffe1f3ec89548b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 9 Oct 2024 15:39:06 +0300 Subject: [PATCH 018/175] set the active symbol --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 49 +++++++++++++++++++++++++-- services/registration/voucher_set | 2 +- services/registration/voucher_set.vis | 2 ++ services/registration/voucher_set_swa | 2 +- 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 4c152b9..3b79ea2 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -95,6 +95,7 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers) ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) + ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index b634f91..fbb254b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1058,6 +1058,13 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error + store := h.userdataStore + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + inputStr := string(input) if inputStr == "0" || inputStr == "99" { @@ -1067,7 +1074,6 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") // Initialize the store and prefix database - store := h.userdataStore prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) // Retrieve the voucher symbol list @@ -1111,8 +1117,12 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } } - // If a match is found, return the symbol and balance + // If a match is found, write the temporary sym , then return the symbol and balance if matchedSymbol != "" && matchedBalance != "" { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) + if err != nil { + return res, err + } res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) } else { @@ -1121,3 +1131,38 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } + +// SetVoucher retrieves the temporary voucher, sets it as the active voucher and +// clears the temporary voucher/sym +func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + store := h.userdataStore + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + // get the current temporary symbol + temporarySym, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM) + if err != nil { + return res, err + } + + // set the active symbol + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym)) + if err != nil { + return res, err + } + + // reset the temporary symbol + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")) + if err != nil { + return res, err + } + + res.Content = string(temporarySym) + + return res, nil +} diff --git a/services/registration/voucher_set b/services/registration/voucher_set index 838c1ef..e90d2e5 100644 --- a/services/registration/voucher_set +++ b/services/registration/voucher_set @@ -1 +1 @@ -Success! symbol is now your active voucher. \ No newline at end of file +Success! {{.set_voucher}} is now your active voucher. \ No newline at end of file diff --git a/services/registration/voucher_set.vis b/services/registration/voucher_set.vis index 832ef22..e3e81f5 100644 --- a/services/registration/voucher_set.vis +++ b/services/registration/voucher_set.vis @@ -1,3 +1,5 @@ +LOAD set_voucher 12 +MAP set_voucher MOUT back 0 MOUT quit 9 HALT diff --git a/services/registration/voucher_set_swa b/services/registration/voucher_set_swa index 320a315..97d3fde 100644 --- a/services/registration/voucher_set_swa +++ b/services/registration/voucher_set_swa @@ -1 +1 @@ -Hongera! symbol ni Sarafu inayotumika sasa. \ No newline at end of file +Hongera! {{.set_voucher}} ni Sarafu inayotumika sasa. \ No newline at end of file From 7c08a0f0afad4f01b52321f17e4ef4a7f458a635 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 10 Oct 2024 14:48:23 +0300 Subject: [PATCH 019/175] add temp and active balance --- internal/handlers/ussd/menuhandler.go | 19 +++++++++++++++++++ internal/utils/db.go | 2 ++ 2 files changed, 21 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index fbb254b..e459975 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1123,6 +1123,10 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r if err != nil { return res, err } + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance)) + if err != nil { + return res, err + } res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) } else { @@ -1149,18 +1153,33 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re if err != nil { return res, err } + // get the current temporary balance + temporaryBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL) + if err != nil { + return res, err + } // set the active symbol err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym)) if err != nil { return res, err } + // set the active balance + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(temporaryBal)) + if err != nil { + return res, err + } // reset the temporary symbol err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")) if err != nil { return res, err } + // reset the temporary balance + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")) + if err != nil { + return res, err + } res.Content = string(temporarySym) diff --git a/internal/utils/db.go b/internal/utils/db.go index f70e08b..45e7681 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -26,6 +26,8 @@ const ( DATA_VOUCHER_LIST DATA_TEMPORARY_SYM DATA_ACTIVE_SYM + DATA_TEMPORARY_BAL + DATA_ACTIVE_BAL ) func typToBytes(typ DataTyp) []byte { From ea2df552958f5b9fb53dcc916f4ac9a635f28547 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 10 Oct 2024 15:18:13 +0300 Subject: [PATCH 020/175] use go-vise v0.2.0 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e3a441a..cdadce5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.22.6 require ( - git.defalsify.org/vise.git v0.1.0-rc.3.0.20240926120105-89b0529cf7ac + git.defalsify.org/vise.git v0.2.0 github.com/alecthomas/assert/v2 v2.2.2 github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 diff --git a/go.sum b/go.sum index 3f7a262..d8a1553 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihc git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= git.defalsify.org/vise.git v0.1.0-rc.3.0.20240926120105-89b0529cf7ac h1:D4KI22KWXT8S66sHIjWhTBX6SXRfnd7j8VErq3PPbok= git.defalsify.org/vise.git v0.1.0-rc.3.0.20240926120105-89b0529cf7ac/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= +git.defalsify.org/vise.git v0.2.0 h1:X2ZgiGRq4C+9qOlDMP0b/oE5QHjVQNT4aEFZB88ST0Q= +git.defalsify.org/vise.git v0.2.0/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= From 6c93fb76b11eacfbd44a27436ae0d6ea1a0bbcb8 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 11 Oct 2024 09:36:43 +0300 Subject: [PATCH 021/175] correctly added the flag and the flag count --- cmd/africastalking/main.go | 2 +- cmd/async/main.go | 2 +- cmd/http/main.go | 2 +- cmd/main.go | 2 +- services/registration/pp.csv | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index c24c4b1..4dbf555 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -87,7 +87,7 @@ func main() { cfg := engine.Config{ Root: "root", OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(17), } if engineDebug { diff --git a/cmd/async/main.go b/cmd/async/main.go index 09236fd..eda54f8 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -60,7 +60,7 @@ func main() { cfg := engine.Config{ Root: "root", OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(17), } if engineDebug { diff --git a/cmd/http/main.go b/cmd/http/main.go index 6b868ed..3708f92 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -48,7 +48,7 @@ func main() { cfg := engine.Config{ Root: "root", OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(17), } if engineDebug { diff --git a/cmd/main.go b/cmd/main.go index 9db5e0a..b0cdb80 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -40,7 +40,7 @@ func main() { Root: "root", SessionId: sessionId, OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(17), } resourceDir := scriptDir diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 2a7bb21..1391771 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -12,5 +12,6 @@ flag,flag_invalid_amount,18,this is set when the given transaction amount is inv flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN flag,flag_valid_pin,20,this is set when the given PIN is valid flag,flag_allow_update,21,this is set to allow a user to update their profile data -flag,flag_incorrect_voucher,22,this is set when the selected voucher is invalid +flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid +flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid From 8f834b3d76e44a8c854eefccfa19bb8be397d1da Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 11 Oct 2024 09:41:47 +0300 Subject: [PATCH 022/175] ensure that a user is authorized --- services/registration/voucher_set.vis | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/registration/voucher_set.vis b/services/registration/voucher_set.vis index e3e81f5..e75c693 100644 --- a/services/registration/voucher_set.vis +++ b/services/registration/voucher_set.vis @@ -1,3 +1,6 @@ +LOAD reset_incorrect 6 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH _ flag_account_authorized 0 LOAD set_voucher 12 MAP set_voucher MOUT back 0 From 672eebb8fb43ac8947730532a5d0830c1da93587 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 11 Oct 2024 09:42:08 +0300 Subject: [PATCH 023/175] display the balance based on the symbol --- internal/handlers/ussd/menuhandler.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index e459975..fb1db3f 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -660,11 +660,28 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - balance, err := h.accountService.CheckBalance(string(publicKey)) + // check if the user has an active sym + activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) if err != nil { - return res, nil + if db.IsNotFound(err) { + logg.Printf(logging.LVL_INFO, "Using the default sym to fetch balance") + balance, err := h.accountService.CheckBalance(string(publicKey)) + if err != nil { + return res, err + } + + res.Content = balance + + return res, nil + } } - res.Content = balance + + activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + if err != nil { + return res, err + } + + res.Content = fmt.Sprintf("%s %s", activeBal, activeSym) return res, nil } From a5b1c5b74e9c7da4f650a9c6d1b77f4d1ae4f379 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 14:32:40 +0300 Subject: [PATCH 024/175] cleaned up the code --- internal/handlers/ussd/menuhandler.go | 65 +++++++++++++-------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index fb1db3f..4196325 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -233,28 +233,6 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt return res, nil } -// GetVoucherList fetches the list of vouchers and formats them -// checks whether they are stored internally before calling the API -func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - - // check if the vouchers exist internally and if not - // fetch from the API - - // Read vouchers from the store - store := h.userdataStore - prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) - - voucherData, err := prefixdb.Get(ctx, []byte("tokens")) - if err != nil { - return res, nil - } - - res.Content = string(voucherData) - - return res, nil -} - func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -1057,13 +1035,13 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) voucherSymbolList := strings.Join(numberedSymbols, "\n") voucherBalanceList := strings.Join(numberedBalances, "\n") - prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) - err = prefixdb.Put(ctx, []byte("tokens"), []byte(voucherSymbolList)) + prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) + err = prefixdb.Put(ctx, []byte("sym"), []byte(voucherSymbolList)) if err != nil { return res, nil } - err = prefixdb.Put(ctx, []byte(voucherSymbolList), []byte(voucherBalanceList)) + err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList)) if err != nil { return res, nil } @@ -1071,6 +1049,24 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } +// GetVoucherList fetches the list of vouchers and formats them +func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + + // Read vouchers from the store + store := h.userdataStore + prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) + + voucherData, err := prefixdb.Get(ctx, []byte("sym")) + if err != nil { + return res, nil + } + + res.Content = string(voucherData) + + return res, nil +} + // ViewVoucher retrieves the token holding and balance from the subprefixDB func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1082,25 +1078,25 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, fmt.Errorf("missing session") } + flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") + inputStr := string(input) if inputStr == "0" || inputStr == "99" { + res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) return res, nil } - flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") - - // Initialize the store and prefix database - prefixdb := storage.NewSubPrefixDb(store, []byte("token_holdings")) + prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) // Retrieve the voucher symbol list - voucherSymbolList, err := prefixdb.Get(ctx, []byte("tokens")) + voucherSymbolList, err := prefixdb.Get(ctx, []byte("sym")) if err != nil { return res, fmt.Errorf("failed to retrieve voucher symbol list: %v", err) } // Retrieve the voucher balance list - voucherBalanceList, err := prefixdb.Get(ctx, []byte(voucherSymbolList)) + voucherBalanceList, err := prefixdb.Get(ctx, []byte("bal")) if err != nil { return res, fmt.Errorf("failed to retrieve voucher balance list: %v", err) } @@ -1134,7 +1130,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } } - // If a match is found, write the temporary sym , then return the symbol and balance + // If a match is found, write the temporary sym, then return the symbol and balance if matchedSymbol != "" && matchedBalance != "" { err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) if err != nil { @@ -1153,8 +1149,9 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } -// SetVoucher retrieves the temporary voucher, sets it as the active voucher and -// clears the temporary voucher/sym +// SetVoucher retrieves the temporary sym and balance, +// sets them as the active data and +// clears the temporary data func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error From e9684fcf45b4fc3873d84dda5d932bf7721b193b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 16:28:02 +0300 Subject: [PATCH 025/175] check whether the active symbol is empty --- internal/handlers/ussd/menuhandler.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4196325..d6d8102 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -652,6 +652,20 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, nil } + + return res, err + } + + if len(activeSym) == 0 { + logg.Printf(logging.LVL_INFO, "Using the default sym to fetch balance") + balance, err := h.accountService.CheckBalance(string(publicKey)) + if err != nil { + return res, err + } + + res.Content = balance + + return res, nil } activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) From b6d24bf92938fce74eada07f3e454fcf71e32fa0 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 16:29:12 +0300 Subject: [PATCH 026/175] update the TestCheckBalance --- internal/handlers/ussd/menuhandler_test.go | 83 ++++++++++++++++------ 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 1ce8b51..21edb3d 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1560,33 +1560,72 @@ func TestValidateRecipient(t *testing.T) { } func TestCheckBalance(t *testing.T) { - - mockDataStore := new(mocks.MockUserDataStore) - sessionId := "session123" - publicKey := "0X13242618721" - balance := "0.003 CELO" - - expectedResult := resource.Result{ - Content: "0.003 CELO", + tests := []struct { + name string + sessionId string + publicKey string + activeSym string + activeBal string + expectedResult resource.Result + expectError bool + }{ + { + name: "User with active sym", + sessionId: "session456", + publicKey: "0X98765432109", + activeSym: "ETH", + activeBal: "1.5", + expectedResult: resource.Result{Content: "1.5 ETH"}, + expectError: false, + }, + { + name: "User without active sym", + sessionId: "session123", + publicKey: "0X13242618721", + activeSym: "", + activeBal: "", + expectedResult: resource.Result{Content: "0.003 CELO"}, + expectError: false, + }, } - mockCreateAccountService := new(mocks.MockAccountService) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + mockAccountService := new(mocks.MockAccountService) + ctx := context.WithValue(context.Background(), "SessionId", tt.sessionId) - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + h := &Handlers{ + userdataStore: mockDataStore, + accountService: mockAccountService, + } - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, - //flagManager: fm.parser, + // Mock calls for public key + mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(tt.publicKey), nil) + + if tt.activeSym == "" { + // Mock for user without active sym + mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte{}, db.ErrNotFound{}) + mockAccountService.On("CheckBalance", tt.publicKey).Return("0.003 CELO", nil) + } else { + // Mock for user with active sym + mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) + mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) + } + + res, err := h.CheckBalance(ctx, "check_balance", []byte("123456")) + + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, res, "Result should match expected output") + } + + mockDataStore.AssertExpectations(t) + mockAccountService.AssertExpectations(t) + }) } - //mock call operations - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(balance, nil) - - res, _ := h.CheckBalance(ctx, "check_balance", []byte("123456")) - - assert.Equal(t, res, expectedResult, "Result should contain flag(s) that have been reset") - } func TestGetProfile(t *testing.T) { From a9f986797677d6430ad7d783dc0241b35ca62674 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 17:36:00 +0300 Subject: [PATCH 027/175] added the TestSetVoucher --- internal/handlers/ussd/menuhandler_test.go | 35 +++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 21edb3d..9159722 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1782,5 +1782,38 @@ func TestConfirmPin(t *testing.T) { }) } - +} + +func TestSetVoucher(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + temporarySym := []byte("tempSym") + temporaryBal := []byte("tempBal") + + // Set expectations for the mock data store + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return(temporarySym, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return(temporaryBal, nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, temporarySym).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, temporaryBal).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil) + + h := &Handlers{ + userdataStore: mockDataStore, + } + + // Call the method under test + res, err := h.SetVoucher(ctx, "someSym", []byte{}) + + // Assert that no errors occurred + assert.NoError(t, err) + + // Assert that the result content is correct + assert.Equal(t, string(temporarySym), res.Content) + + // Assert that expectations were met + mockDataStore.AssertExpectations(t) } From 7fe8f0b7d578d1541207a8604a909c08b8124a68 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 18:03:13 +0300 Subject: [PATCH 028/175] updated the args under FetchVouchers --- internal/mocks/servicemock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 8fbde0f..f8f638a 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -26,6 +26,6 @@ func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, erro } func (m *MockAccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { - args := m.Called() + args := m.Called(publicKey) return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) } From f5dbfe553d90ee2e57cf7371ff66486828ccfbea Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 20:06:00 +0300 Subject: [PATCH 029/175] use the check_balance for the max_amount --- internal/handlers/handlerservice.go | 1 - services/registration/amount | 2 +- services/registration/amount.vis | 4 ++-- services/registration/amount_swa | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 3b79ea2..0d891f1 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -69,7 +69,6 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance) ls.DbRs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient) ls.DbRs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset) - ls.DbRs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount) ls.DbRs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount) ls.DbRs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount) ls.DbRs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient) diff --git a/services/registration/amount b/services/registration/amount index 9142aba..d3e8602 100644 --- a/services/registration/amount +++ b/services/registration/amount @@ -1,2 +1,2 @@ -Maximum amount: {{.max_amount}} +Maximum amount: {{.check_balance}} Enter amount: \ No newline at end of file diff --git a/services/registration/amount.vis b/services/registration/amount.vis index b491fab..5a02ff9 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -1,6 +1,6 @@ LOAD reset_transaction_amount 0 -LOAD max_amount 10 -MAP max_amount +LOAD check_balance 48 +MAP check_balance MOUT back 0 HALT LOAD validate_amount 64 diff --git a/services/registration/amount_swa b/services/registration/amount_swa index 0c8cf01..d071f63 100644 --- a/services/registration/amount_swa +++ b/services/registration/amount_swa @@ -1,2 +1,2 @@ -Kiwango cha juu: {{.max_amount}} +Kiwango cha juu: {{.check_balance}} Weka kiwango: \ No newline at end of file From 7df77a134307823e058ca05541a004283189d0e1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 12 Oct 2024 20:07:06 +0300 Subject: [PATCH 030/175] updated the ValidateAmount to also check the active symbol, updated tests --- internal/handlers/ussd/menuhandler.go | 88 +++++++++------------- internal/handlers/ussd/menuhandler_test.go | 76 +++++++++---------- 2 files changed, 72 insertions(+), 92 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d6d8102..3e164af 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -761,74 +761,60 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input return res, nil } -// MaxAmount gets the current balance from the API and sets it as -// the result content. -func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var err error - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - - balance, err := h.accountService.CheckBalance(string(publicKey)) - if err != nil { - return res, nil - } - - res.Content = balance - - return res, nil -} - // ValidateAmount ensures that the given input is a valid amount and that // it is not more than the current balance. func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") - store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - - amountStr := string(input) - - balanceStr, err := h.accountService.CheckBalance(string(publicKey)) + publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { return res, err } - res.Content = balanceStr - // Parse the balance - balanceParts := strings.Split(balanceStr, " ") - if len(balanceParts) != 2 { - return res, fmt.Errorf("unexpected balance format: %s", balanceStr) - } - balanceValue, err := strconv.ParseFloat(balanceParts[0], 64) - if err != nil { - return res, fmt.Errorf("failed to parse balance: %v", err) + // retrieve the active symbol + activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + useActiveSymbol := err == nil && len(activeSym) > 0 + + var balanceValue float64 + if useActiveSymbol { + // If active symbol is set, retrieve its balance + activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + if err != nil { + return res, fmt.Errorf("failed to get active balance: %v", err) + } + balanceValue, err = strconv.ParseFloat(string(activeBal), 64) + if err != nil { + return res, fmt.Errorf("failed to parse active balance: %v", err) + } + } else { + // If no active symbol, use the current balance from the API + balanceStr, err := h.accountService.CheckBalance(string(publicKey)) + if err != nil { + return res, fmt.Errorf("failed to check balance: %v", err) + } + res.Content = balanceStr + + // Parse the balance string + balanceParts := strings.Split(balanceStr, " ") + if len(balanceParts) != 2 { + return res, fmt.Errorf("unexpected balance format: %s", balanceStr) + } + balanceValue, err = strconv.ParseFloat(balanceParts[0], 64) + if err != nil { + return res, fmt.Errorf("failed to parse balance: %v", err) + } } - // Extract numeric part from input - re := regexp.MustCompile(`^(\d+(\.\d+)?)\s*(?:CELO)?$`) - matches := re.FindStringSubmatch(strings.TrimSpace(amountStr)) - if len(matches) < 2 { - res.FlagSet = append(res.FlagSet, flag_invalid_amount) - res.Content = amountStr - return res, nil - } - - inputAmount, err := strconv.ParseFloat(matches[1], 64) + // Extract numeric part from the input amount + amountStr := strings.TrimSpace(string(input)) + inputAmount, err := strconv.ParseFloat(amountStr, 64) if err != nil { res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr @@ -841,12 +827,12 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) return res, nil } - res.Content = fmt.Sprintf("%.3f", inputAmount) // Format to 3 decimal places err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(amountStr)) if err != nil { return res, err } + res.Content = fmt.Sprintf("%.3f", inputAmount) return res, nil } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 9159722..4fd9d22 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -434,34 +434,6 @@ func TestCheckIdentifier(t *testing.T) { } } -func TestMaxAmount(t *testing.T) { - mockStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - - // Define test data - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - publicKey := "0xcasgatweksalw1018221" - expectedBalance := "0.003CELO" - - // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockCreateAccountService.On("CheckBalance", publicKey).Return(expectedBalance, nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - accountService: mockCreateAccountService, - } - - // Call the method - res, _ := h.MaxAmount(ctx, "max_amount", []byte("check_balance")) - - //Assert that the balance that was set as the result content is what was returned by Check Balance - assert.Equal(t, expectedBalance, res.Content) - -} - func TestGetSender(t *testing.T) { mockStore := new(mocks.MockUserDataStore) @@ -1444,59 +1416,81 @@ func TestValidateAmount(t *testing.T) { name string input []byte publicKey []byte + activeSym []byte + activeBal []byte balance string expectedResult resource.Result }{ { - name: "Test with valid amount", + name: "Test with valid amount and active symbol", input: []byte("0.001"), - balance: "0.003 CELO", publicKey: []byte("0xrqeqrequuq"), + activeSym: []byte("CELO"), + activeBal: []byte("0.003"), expectedResult: resource.Result{ Content: "0.001", }, }, { - name: "Test with amount larger than balance", + name: "Test with amount larger than active balance", input: []byte("0.02"), - balance: "0.003 CELO", publicKey: []byte("0xrqeqrequuq"), + activeSym: []byte("CELO"), + activeBal: []byte("0.003"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, Content: "0.02", }, }, { - name: "Test with invalid amount", + name: "Test with invalid amount format", input: []byte("0.02ms"), - balance: "0.003 CELO", publicKey: []byte("0xrqeqrequuq"), + balance: "0.003 CELO", expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, Content: "0.02ms", }, }, + { + name: "Test fallback to current balance without active symbol", + input: []byte("0.001"), + publicKey: []byte("0xrqeqrequuq"), + balance: "0.003 CELO", + expectedResult: resource.Result{ + Content: "0.001", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - + // Mock behavior for public key retrieval mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil) - mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil) + + // Mock behavior for active symbol and balance retrieval (if present) + if len(tt.activeSym) > 0 { + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(tt.activeSym, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) + } else { + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(nil, fmt.Errorf("not found")) + mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil) + } + + // Mock behavior for storing the amount (if valid) mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe() // Call the method under test res, _ := h.ValidateAmount(ctx, "test_validate_amount", tt.input) - // Assert that no errors occurred + // Assert no errors occurred assert.NoError(t, err) - //Assert that the account created flag has been set to the result - assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") + // Assert the result matches the expected result + assert.Equal(t, tt.expectedResult, res, "Expected result should match actual result") - // Assert that expectations were met + // Assert all expectations were met mockDataStore.AssertExpectations(t) - }) } } From 65794c1b20703ce87b138c290f49c88204f50b6d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 14 Oct 2024 23:17:17 +0300 Subject: [PATCH 031/175] add api calls flag --- services/registration/pp.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index fd552e4..af5b3dc 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -14,3 +14,4 @@ flag,flag_valid_pin,20,this is set when the given PIN is valid flag,flag_allow_update,21,this is set to allow a user to update their profile data flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid +flag,flag_api_call_error,25,this is set when communication to an external service fails From be6391686faddb013ffd141d29f7e212df042fd1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 14 Oct 2024 23:17:57 +0300 Subject: [PATCH 032/175] update return type --- internal/mocks/servicemock.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 9fb6d3e..d828045 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -15,12 +15,12 @@ func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { return args.Get(0).(*models.AccountResponse), args.Error(1) } -func (m *MockAccountService) CheckBalance(publicKey string) (string, error) { +func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { args := m.Called(publicKey) - return args.String(0), args.Error(1) + return args.Get(0).(*models.BalanceResponse), args.Error(1) } -func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, error) { +func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { args := m.Called(trackingId) - return args.String(0), args.Error(1) + return args.Get(0).(*models.TrackStatusResponse), args.Error(1) } \ No newline at end of file From 952da86931ac42f1e2a5831ccd318d6a1216e3ae Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 14 Oct 2024 23:18:42 +0300 Subject: [PATCH 033/175] update balance nodes --- services/registration/community_balance | 2 +- services/registration/community_balance.vis | 10 ++++++++-- services/registration/my_balance | 2 +- services/registration/my_balance.vis | 10 ++++++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/services/registration/community_balance b/services/registration/community_balance index 5d292ee..f8f8318 100644 --- a/services/registration/community_balance +++ b/services/registration/community_balance @@ -1 +1 @@ -Your community balance is: 0.00SRF \ No newline at end of file +{{.fetch_custodial_balances}} \ No newline at end of file diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index 151c6d8..85ae93a 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -1,5 +1,11 @@ -LOAD reset_incorrect 0 +LOAD reset_incorrect 6 +LOAD fetch_custodial_balances 0 +CATCH api_failure flag_api_call_error 1 +MAP fetch_custodial_balances CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 -LOAD quit_with_balance 0 +MOUT back 0 +MOUT quit 9 HALT +INCMP _ 0 +INCMP quit 9 diff --git a/services/registration/my_balance b/services/registration/my_balance index 15c5901..f8f8318 100644 --- a/services/registration/my_balance +++ b/services/registration/my_balance @@ -1 +1 @@ -Your balance is: 0.00 SRF \ No newline at end of file +{{.fetch_custodial_balances}} \ No newline at end of file diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index 151c6d8..85ae93a 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -1,5 +1,11 @@ -LOAD reset_incorrect 0 +LOAD reset_incorrect 6 +LOAD fetch_custodial_balances 0 +CATCH api_failure flag_api_call_error 1 +MAP fetch_custodial_balances CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 -LOAD quit_with_balance 0 +MOUT back 0 +MOUT quit 9 HALT +INCMP _ 0 +INCMP quit 9 From c641a0c6696122df0c22deafc28bd323d15846a7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 14 Oct 2024 23:19:09 +0300 Subject: [PATCH 034/175] add api failure nodes --- services/registration/api_failure | 1 + services/registration/api_failure.vis | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 services/registration/api_failure create mode 100644 services/registration/api_failure.vis diff --git a/services/registration/api_failure b/services/registration/api_failure new file mode 100644 index 0000000..06d2d9e --- /dev/null +++ b/services/registration/api_failure @@ -0,0 +1 @@ +Failed to connect to the custodial service.Please try again. \ No newline at end of file diff --git a/services/registration/api_failure.vis b/services/registration/api_failure.vis new file mode 100644 index 0000000..e045355 --- /dev/null +++ b/services/registration/api_failure.vis @@ -0,0 +1,5 @@ +MOUT retry 0 +MOUT quit 9 +HALT +INCMP _ 0 +INCMP quit 9 From 1927544533e9eeb2a785d66122df926b42c0d943 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:40:40 +0300 Subject: [PATCH 035/175] reset authorized flag --- services/registration/balances.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/balances.vis b/services/registration/balances.vis index dd14501..aef397f 100644 --- a/services/registration/balances.vis +++ b/services/registration/balances.vis @@ -1,4 +1,5 @@ LOAD reset_account_authorized 0 +RELOAD reset_account_authorized MOUT my_balance 1 MOUT community_balance 2 MOUT back 0 From 26d315b032345991c0bd9988fa1d6aadc03592b3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:42:20 +0300 Subject: [PATCH 036/175] add check for api call failure --- services/registration/main.vis | 1 + services/registration/root.vis | 2 ++ 2 files changed, 3 insertions(+) diff --git a/services/registration/main.vis b/services/registration/main.vis index d883dca..0379078 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -1,5 +1,6 @@ LOAD check_balance 64 RELOAD check_balance +CATCH api_failure flag_api_call_error 1 MAP check_balance MOUT send 1 MOUT vouchers 2 diff --git a/services/registration/root.vis b/services/registration/root.vis index 6e3b79d..02ef9e9 100644 --- a/services/registration/root.vis +++ b/services/registration/root.vis @@ -1,6 +1,8 @@ CATCH select_language flag_language_set 0 CATCH terms flag_account_created 0 LOAD check_account_status 0 +RELOAD check_account_status +CATCH api_failure flag_api_call_error 1 CATCH account_pending flag_account_pending 1 CATCH create_pin flag_pin_set 0 CATCH main flag_account_success 1 From 4a627730980f6dbfb984d2abe99fa52d5eb8ee19 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:44:50 +0300 Subject: [PATCH 037/175] add handler fetching custodial balances --- internal/handlers/handlerservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 4cedd26..561b76a 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -82,7 +82,6 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("save_location", ussdHandlers.SaveLocation) ls.DbRs.AddLocalFunc("save_yob", ussdHandlers.SaveYob) ls.DbRs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings) - ls.DbRs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance) ls.DbRs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized) ls.DbRs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate) ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo) @@ -93,6 +92,7 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) + ls.DbRs.AddLocalFunc("fetch_custodial_balances", ussdHandlers.FetchCustodialBalances) return ussdHandlers, nil } From df7788dd0b91dfd52648f74bd599a0d0a059a29b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:48:14 +0300 Subject: [PATCH 038/175] return actual reponses on the api calls --- internal/handlers/server/accountservice.go | 44 +++++++--------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index f4375a1..1ddf7e7 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -10,16 +10,14 @@ import ( ) type AccountServiceInterface interface { - CheckBalance(publicKey string) (string, error) + CheckBalance(publicKey string) (*models.BalanceResponse, error) CreateAccount() (*models.AccountResponse, error) - CheckAccountStatus(trackingId string) (string, error) + CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) } type AccountService struct { } - - // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. // // Parameters: @@ -27,64 +25,51 @@ type AccountService struct { // CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the // AccountResponse struct can be used here to check the account status during a transaction. // -// // Returns: // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -// -func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) { +func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { resp, err := http.Get(config.TrackStatusURL + trackingId) if err != nil { - return "", err + return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return "", err + return nil, err } - var trackResp models.TrackStatusResponse err = json.Unmarshal(body, &trackResp) if err != nil { - return "", err + return nil, err } - - status := trackResp.Result.Transaction.Status - - return status, nil + return &trackResp, nil } - // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. -func (as *AccountService) CheckBalance(publicKey string) (string, error) { - +func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { resp, err := http.Get(config.BalanceURL + publicKey) if err != nil { - return "0.0", err + return nil, err } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) if err != nil { - return "0.0", err + return nil, err } - var balanceResp models.BalanceResponse err = json.Unmarshal(body, &balanceResp) if err != nil { - return "0.0", err + return nil, err } - - balance := balanceResp.Result.Balance - return balance, nil + return &balanceResp, nil } - -//CreateAccount creates a new account in the custodial system. +// CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. // If there is an error during the request or processing, this will be nil. @@ -96,17 +81,14 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return nil, err } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } - var accountResp models.AccountResponse err = json.Unmarshal(body, &accountResp) if err != nil { return nil, err } - return &accountResp, nil } From d638aba85eeab1dde39837bc36f5ef66abdca0c6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:50:28 +0300 Subject: [PATCH 039/175] update tests --- internal/handlers/ussd/menuhandler_test.go | 488 +++++++++++++++------ 1 file changed, 362 insertions(+), 126 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 83e6f0c..82cca01 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -7,8 +7,11 @@ import ( "log" "path" "testing" + "time" + "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/db" + "git.defalsify.org/vise.git/lang" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" @@ -25,10 +28,53 @@ var ( flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") ) -func TestCreateAccount(t *testing.T) { +type Transaction struct { + CreatedAt time.Time `json:"createdAt"` + Status string `json:"status"` + TransferValue json.Number `json:"transferValue"` + TxHash string `json:"txHash"` + TxType string `json:"txType"` +} +func TestNewHandlers(t *testing.T) { fm, err := NewFlagManager(flagsPath) + if err != nil { + t.Logf(err.Error()) + } + t.Run("Valid UserDataStore", func(t *testing.T) { + mockStore := &mocks.MockUserDataStore{} + handlers, err := NewHandlers(fm.parser, mockStore) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if handlers == nil { + t.Fatal("expected handlers to be non-nil") + } + if handlers.userdataStore == nil { + t.Fatal("expected userdataStore to be set in handlers") + } + }) + // Test case for nil userdataStore + t.Run("Nil UserDataStore", func(t *testing.T) { + appFlags := &asm.FlagParser{} + + handlers, err := NewHandlers(appFlags, nil) + + if err == nil { + t.Fatal("expected an error, got none") + } + if handlers != nil { + t.Fatal("expected handlers to be nil") + } + if err.Error() != "cannot create handler with nil userdata store" { + t.Fatalf("expected specific error, got %v", err) + } + }) +} + +func TestCreateAccount(t *testing.T) { + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } @@ -442,7 +488,10 @@ func TestMaxAmount(t *testing.T) { sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) publicKey := "0xcasgatweksalw1018221" - expectedBalance := "0.003CELO" + + expectedBalance := &models.BalanceResponse{ + Ok: true, + } // Set up the expected behavior of the mock mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) @@ -458,7 +507,7 @@ func TestMaxAmount(t *testing.T) { res, _ := h.MaxAmount(ctx, "max_amount", []byte("check_balance")) //Assert that the balance that was set as the result content is what was returned by Check Balance - assert.Equal(t, expectedBalance, res.Content) + assert.Equal(t, expectedBalance.Result.Balance, res.Content) } @@ -537,12 +586,10 @@ func TestGetRecipient(t *testing.T) { func TestGetFlag(t *testing.T) { fm, err := NewFlagManager(flagsPath) expectedFlag := uint32(9) - if err != nil { t.Logf(err.Error()) } flag, err := fm.GetFlag("flag_account_created") - if err != nil { t.Logf(err.Error()) } @@ -1012,53 +1059,109 @@ func TestVerifyPin(t *testing.T) { func TestCheckAccountStatus(t *testing.T) { fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - sessionId := "session123" flag_account_success, _ := fm.GetFlag("flag_account_success") flag_account_pending, _ := fm.GetFlag("flag_account_pending") + flag_api_error, _ := fm.GetFlag("flag_api_call_error") ctx := context.WithValue(context.Background(), "SessionId", sessionId) - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, - flagManager: fm.parser, - } tests := []struct { name string input []byte - status string + response *models.TrackStatusResponse expectedResult resource.Result }{ { - name: "Test when account status is Success", - input: []byte("TrackingId1234"), - status: "SUCCESS", + name: "Test when account status is Success", + input: []byte("TrackingId1234"), + response: &models.TrackStatusResponse{ + Ok: true, + Result: struct { + Transaction struct { + CreatedAt time.Time "json:\"createdAt\"" + Status string "json:\"status\"" + TransferValue json.Number "json:\"transferValue\"" + TxHash string "json:\"txHash\"" + TxType string "json:\"txType\"" + } + }{ + Transaction: Transaction{ + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", + }, + }, + }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_success}, - FlagReset: []uint32{flag_account_pending}, + FlagReset: []uint32{flag_api_error, flag_account_pending}, + }, + }, + { + name: "Test when fetching account status is not Success", + input: []byte("TrackingId1234"), + response: &models.TrackStatusResponse{ + Ok: false, + }, + expectedResult: resource.Result{ + FlagSet: []uint32{flag_api_error}, + }, + }, + { + name: "Test when checking account status api call is a SUCCESS but an account is not yet ready", + input: []byte("TrackingId1234"), + response: &models.TrackStatusResponse{ + Ok: true, + Result: struct { + Transaction struct { + CreatedAt time.Time "json:\"createdAt\"" + Status string "json:\"status\"" + TransferValue json.Number "json:\"transferValue\"" + TxHash string "json:\"txHash\"" + TxType string "json:\"txType\"" + } + }{ + Transaction: Transaction{ + CreatedAt: time.Now(), + Status: "IN_NETWORK", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", + }, + }, + }, + expectedResult: resource.Result{ + FlagSet: []uint32{flag_account_pending}, + FlagReset: []uint32{flag_api_error, flag_account_success}, }, }, } - - typ := utils.DATA_TRACKING_ID for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + mockCreateAccountService := new(mocks.MockAccountService) + h := &Handlers{ + userdataStore: mockDataStore, + accountService: mockCreateAccountService, + flagManager: fm.parser, + } + + status := tt.response.Result.Transaction.Status // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return(tt.input, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TRACKING_ID).Return(tt.input, nil) - mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.status, nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(tt.status)).Return(nil) + mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() // Call the method under test - res, _ := h.CheckAccountStatus(ctx, "check_status", tt.input) + res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) // Assert that no errors occurred assert.NoError(t, err) @@ -1358,66 +1461,6 @@ func TestIsValidPIN(t *testing.T) { } } -func TestQuitWithBalance(t *testing.T) { - fm, err := NewFlagManager(flagsPath) - - if err != nil { - t.Logf(err.Error()) - } - flag_account_authorized, _ := fm.parser.GetFlag("flag_account_authorized") - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - - sessionId := "session123" - - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, - flagManager: fm.parser, - } - tests := []struct { - name string - input []byte - publicKey []byte - balance string - expectedResult resource.Result - }{ - { - name: "Test quit with balance", - balance: "0.02CELO", - publicKey: []byte("0xrqeqrequuq"), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_account_authorized}, - Content: fmt.Sprintf("Your account balance is %s", "0.02CELO"), - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil) - mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil) - - // Call the method under test - res, _ := h.QuitWithBalance(ctx, "test_quit_with_balance", tt.input) - - // Assert that no errors occurred - assert.NoError(t, err) - - //Assert that the account created flag has been set to the result - assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - - }) - } -} - func TestValidateAmount(t *testing.T) { fm, err := NewFlagManager(flagsPath) @@ -1438,25 +1481,43 @@ func TestValidateAmount(t *testing.T) { flagManager: fm.parser, } tests := []struct { - name string - input []byte - publicKey []byte - balance string - expectedResult resource.Result + name string + input []byte + publicKey []byte + balanceResponse *models.BalanceResponse + expectedResult resource.Result }{ { - name: "Test with valid amount", - input: []byte("0.001"), - balance: "0.003 CELO", + name: "Test with valid amount", + input: []byte("0.001"), + balanceResponse: &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, publicKey: []byte("0xrqeqrequuq"), expectedResult: resource.Result{ Content: "0.001", }, }, { - name: "Test with amount larger than balance", - input: []byte("0.02"), - balance: "0.003 CELO", + name: "Test with amount larger than balance", + input: []byte("0.02"), + balanceResponse: &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, publicKey: []byte("0xrqeqrequuq"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, @@ -1464,9 +1525,18 @@ func TestValidateAmount(t *testing.T) { }, }, { - name: "Test with invalid amount", - input: []byte("0.02ms"), - balance: "0.003 CELO", + name: "Test with invalid amount", + input: []byte("0.02ms"), + balanceResponse: &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, publicKey: []byte("0xrqeqrequuq"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, @@ -1479,7 +1549,7 @@ func TestValidateAmount(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil) - mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil) + mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balanceResponse, nil) mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe() // Call the method under test @@ -1558,31 +1628,83 @@ func TestValidateRecipient(t *testing.T) { func TestCheckBalance(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) sessionId := "session123" publicKey := "0X13242618721" - balance := "0.003 CELO" - - expectedResult := resource.Result{ - Content: "0.003 CELO", - } - - mockCreateAccountService := new(mocks.MockAccountService) + fm, _ := NewFlagManager(flagsPath) + flag_api_error, _ := fm.GetFlag("flag_api_call_error") ctx := context.WithValue(context.Background(), "SessionId", sessionId) - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, - //flagManager: fm.parser, + tests := []struct { + name string + balanceResonse *models.BalanceResponse + expectedResult resource.Result + }{ + { + name: "Test when checking a balance is not a success", + balanceResonse: &models.BalanceResponse{ + Ok: false, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, + expectedResult: resource.Result{ + FlagSet: []uint32{flag_api_error}, + }, + }, + { + name: "Test when checking a balance is a success", + balanceResonse: &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, + expectedResult: resource.Result{ + Content: "0.003 CELO", + FlagReset: []uint32{flag_api_error}, + }, + }, } - //mock call operations - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(balance, nil) - res, _ := h.CheckBalance(ctx, "check_balance", []byte("123456")) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, res, expectedResult, "Result should contain flag(s) that have been reset") + mockDataStore := new(mocks.MockUserDataStore) + mockCreateAccountService := new(mocks.MockAccountService) + mockState := state.NewState(16) + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: mockDataStore, + flagManager: fm.parser, + st: mockState, + accountService: mockCreateAccountService, + } + + // Set up the expected behavior of the mock + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) + + // Call the method + res, _ := h.CheckBalance(ctx, "check_balance", []byte("")) + + // Assert that expectations were met + mockDataStore.AssertExpectations(t) + + //Assert that the result set to content is what was expected + assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") + + }) + } } @@ -1592,23 +1714,50 @@ func TestGetProfile(t *testing.T) { mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) + mockState := state.NewState(16) h := &Handlers{ userdataStore: mockDataStore, accountService: mockCreateAccountService, + st: mockState, } - ctx := context.WithValue(context.Background(), "SessionId", sessionId) tests := []struct { - name string - keys []utils.DataTyp - profileInfo []string - result resource.Result + name string + languageCode string + keys []utils.DataTyp + profileInfo []string + result resource.Result }{ { - name: "Test with full profile information", - keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, - profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, + name: "Test with full profile information in eng", + keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, + profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, + languageCode: "eng", + result: resource.Result{ + Content: fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", + "John Doee", "Male", "48", "Kilifi", "Bananas", + ), + }, + }, + { + name: "Test with with profile information in swa ", + keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, + profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, + languageCode: "swa", + result: resource.Result{ + Content: fmt.Sprintf( + "Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n", + "John Doee", "Male", "48", "Kilifi", "Bananas", + ), + }, + }, + { + name: "Test with with profile information with language that is not yet supported", + keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, + profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, + languageCode: "nor", result: resource.Result{ Content: fmt.Sprintf( "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", @@ -1619,9 +1768,14 @@ func TestGetProfile(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx = context.WithValue(ctx, "Language", lang.Language{ + Code: tt.languageCode, + }) for index, key := range tt.keys { - mockDataStore.On("ReadEntry", ctx, sessionId, key).Return([]byte(tt.profileInfo[index]), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, key).Return([]byte(tt.profileInfo[index]), nil).Maybe() } + res, _ := h.GetProfileInfo(ctx, "get_profile_info", []byte("")) // Assert that expectations were met @@ -1778,3 +1932,85 @@ func TestConfirmPin(t *testing.T) { } } + +func TestFetchCustodialBalances(t *testing.T) { + fm, err := NewFlagManager(flagsPath) + if err != nil { + t.Logf(err.Error()) + } + flag_api_error, _ := fm.GetFlag("flag_api_call_error") + + // Define test data + sessionId := "session123" + publicKey := "0X13242618721" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + tests := []struct { + name string + balanceResonse *models.BalanceResponse + expectedResult resource.Result + }{ + { + name: "Test when fetch custodial balances is not a success", + balanceResonse: &models.BalanceResponse{ + Ok: false, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, + expectedResult: resource.Result{ + FlagSet: []uint32{flag_api_error}, + }, + }, + { + name: "Test when fetch custodial balances is a success", + balanceResonse: &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + }, + expectedResult: resource.Result{ + FlagReset: []uint32{flag_api_error}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + mockDataStore := new(mocks.MockUserDataStore) + mockCreateAccountService := new(mocks.MockAccountService) + mockState := state.NewState(16) + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: mockDataStore, + flagManager: fm.parser, + st: mockState, + accountService: mockCreateAccountService, + } + + // Set up the expected behavior of the mock + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) + + // Call the method + res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte("")) + + // Assert that expectations were met + mockDataStore.AssertExpectations(t) + + //Assert that the result set to content is what was expected + assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") + + }) + } +} From bec7e5c69f865d3a57bfb3bf62be2815a4ff6eb8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:57:05 +0300 Subject: [PATCH 040/175] update: fetch balances in a sepate function,show profile information in swahili and english --- internal/handlers/ussd/menuhandler.go | 139 +++++++++++++++++--------- 1 file changed, 89 insertions(+), 50 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8ffecc3..30baf93 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -290,8 +290,6 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res if !ok { return res, fmt.Errorf("missing session") } - - //AccountPin, _ := utils.ReadEntry(ctx, h.userdataStore, sessionId, utils.DATA_ACCOUNT_PIN) store := h.userdataStore AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) if err != nil { @@ -525,9 +523,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res // ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt. func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - res.FlagReset = append(res.FlagReset, flag_incorrect_pin) return res, nil } @@ -539,6 +535,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b flag_account_success, _ := h.flagManager.GetFlag("flag_account_success") flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -550,18 +547,23 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b return res, err } - status, err := h.accountService.CheckAccountStatus(string(trackingId)) + accountStatus, err := h.accountService.CheckAccountStatus(string(trackingId)) if err != nil { fmt.Println("Error checking account status:", err) return res, err } + if !accountStatus.Ok { + res.FlagSet = append(res.FlagSet, flag_api_error) + return res, err + } + res.FlagReset = append(res.FlagReset, flag_api_error) + status := accountStatus.Result.Transaction.Status err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)) if err != nil { return res, nil } - - if status == "SUCCESS" { + if accountStatus.Result.Transaction.Status == "SUCCESS" { res.FlagSet = append(res.FlagSet, flag_account_success) res.FlagReset = append(res.FlagReset, flag_account_pending) } else { @@ -641,6 +643,8 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( var res resource.Result var err error + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") @@ -652,15 +656,60 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - balance, err := h.accountService.CheckBalance(string(publicKey)) + balanceResponse, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } + if !balanceResponse.Ok { + res.FlagSet = append(res.FlagSet, flag_api_error) + return res, nil + } + res.FlagReset = append(res.FlagReset, flag_api_error) + balance := balanceResponse.Result.Balance res.Content = balance return res, nil } +func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + symbol, _ := h.st.Where() + balanceType := strings.Split(symbol, "_")[0] + + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + if err != nil { + return res, err + } + + balanceResponse, err := h.accountService.CheckBalance(string(publicKey)) + if err != nil { + return res, nil + } + if !balanceResponse.Ok { + res.FlagSet = append(res.FlagSet, flag_api_error) + return res, nil + } + res.FlagReset = append(res.FlagReset, flag_api_error) + balance := balanceResponse.Result.Balance + + switch balanceType { + case "my": + res.Content = fmt.Sprintf("Your balance is %s", balance) + case "community": + res.Content = fmt.Sprintf("Your community balance is %s", balance) + default: + break + } + return res, nil +} + // ValidateRecipient validates that the given input is a valid phone number. func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -757,10 +806,11 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res store := h.userdataStore publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - balance, err := h.accountService.CheckBalance(string(publicKey)) + balanceResp, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } + balance := balanceResp.Result.Balance res.Content = balance @@ -785,7 +835,8 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) amountStr := string(input) - balanceStr, err := h.accountService.CheckBalance(string(publicKey)) + balanceRes, err := h.accountService.CheckBalance(string(publicKey)) + balanceStr := balanceRes.Result.Balance if err != nil { return res, err @@ -882,36 +933,6 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res return res, nil } -// QuickWithBalance retrieves the balance for a given public key from the custodial balance API endpoint before -// gracefully exiting the session. -func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var err error - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") - - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - - store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - if err != nil { - return res, err - } - balance, err := h.accountService.CheckBalance(string(publicKey)) - if err != nil { - return res, nil - } - res.Content = l.Get("Your account balance is %s", balance) - res.FlagReset = append(res.FlagReset, flag_account_authorized) - return res, nil -} - // InitiateTransaction returns a confirmation and resets the transaction data // on the gdbm store. func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []byte) (resource.Result, error) { @@ -945,16 +966,23 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, nil } -// GetProfileInfo retrieves and formats the profile information of a user from a Gdbm backed storage. func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + var defaultValue string sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - - // Default value when an entry is not found - defaultValue := "Not Provided" + language, ok := ctx.Value("Language").(lang.Language) + if !ok { + return res, fmt.Errorf("value for 'Language' is not of type lang.Language") + } + code := language.Code + if code == "swa" { + defaultValue = "Haipo" + } else { + defaultValue = "Not Provided" + } // Helper function to handle nil byte slices and convert them to string getEntryOrDefault := func(entry []byte, err error) string { @@ -991,12 +1019,23 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) return res, fmt.Errorf("invalid year of birth: %v", err) } } - - // Format the result - res.Content = fmt.Sprintf( - "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", - name, gender, age, location, offerings, - ) + switch language.Code { + case "eng": + res.Content = fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", + name, gender, age, location, offerings, + ) + case "swa": + res.Content = fmt.Sprintf( + "Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n", + name, gender, age, location, offerings, + ) + default: + res.Content = fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", + name, gender, age, location, offerings, + ) + } return res, nil } From 283793a2ae8abab7cd062c950a3edda5f5b3cc15 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:57:45 +0300 Subject: [PATCH 041/175] add swahili menu option --- services/registration/view_menu_swa | 1 + 1 file changed, 1 insertion(+) create mode 100644 services/registration/view_menu_swa diff --git a/services/registration/view_menu_swa b/services/registration/view_menu_swa new file mode 100644 index 0000000..bd84b19 --- /dev/null +++ b/services/registration/view_menu_swa @@ -0,0 +1 @@ +Angalia Wasifu \ No newline at end of file From 368c25125a99aba8376e11bdf7f9bc3bc98d0a30 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 13:59:49 +0300 Subject: [PATCH 042/175] set flag count to 128 --- cmd/africastalking/main.go | 2 +- cmd/async/main.go | 2 +- cmd/http/main.go | 2 +- cmd/main.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index c24c4b1..051c27b 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -87,7 +87,7 @@ func main() { cfg := engine.Config{ Root: "root", OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(128), } if engineDebug { diff --git a/cmd/async/main.go b/cmd/async/main.go index 09236fd..a945d3c 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -60,7 +60,7 @@ func main() { cfg := engine.Config{ Root: "root", OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(128), } if engineDebug { diff --git a/cmd/http/main.go b/cmd/http/main.go index 6b868ed..1d99c51 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -48,7 +48,7 @@ func main() { cfg := engine.Config{ Root: "root", OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(128), } if engineDebug { diff --git a/cmd/main.go b/cmd/main.go index 9db5e0a..e87fc5a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -40,7 +40,7 @@ func main() { Root: "root", SessionId: sessionId, OutputSize: uint32(size), - FlagCount: uint32(16), + FlagCount: uint32(128), } resourceDir := scriptDir From 8ed98b3e6cc3e3e0d2baf6eda6a26ff919a333b5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 14:53:00 +0300 Subject: [PATCH 043/175] resolve case issue --- internal/handlers/ussd/menuhandler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 4fd9d22..f36814a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -171,7 +171,7 @@ func TestSaveFamilyname(t *testing.T) { mockStore.AssertExpectations(t) } -func TestSaveTemporaryPIn(t *testing.T) { +func TestSaveTemporaryPin(t *testing.T) { fm, err := NewFlagManager(flagsPath) mockStore := new(mocks.MockUserDataStore) if err != nil { From 6c6af5ec210a0b5782c9d17ca3b2d4900dc270cc Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 15:46:02 +0300 Subject: [PATCH 044/175] set a default voucher as the active sym if none exists --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 93 ++++++++++++++++++--------- services/registration/main.vis | 2 + 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 0d891f1..129d851 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -91,6 +91,7 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) + ls.DbRs.AddLocalFunc("set_default_voucher", ussdHandlers.SetDefaultVoucher) ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers) ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 3e164af..af3b99f 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -633,41 +633,13 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( } store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - if err != nil { - return res, err - } - // check if the user has an active sym + // get the active sym and active balance activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) if err != nil { - if db.IsNotFound(err) { - logg.Printf(logging.LVL_INFO, "Using the default sym to fetch balance") - balance, err := h.accountService.CheckBalance(string(publicKey)) - if err != nil { - return res, err - } - - res.Content = balance - - return res, nil - } - return res, err } - if len(activeSym) == 0 { - logg.Printf(logging.LVL_INFO, "Using the default sym to fetch balance") - balance, err := h.accountService.CheckBalance(string(publicKey)) - if err != nil { - return res, err - } - - res.Content = balance - - return res, nil - } - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) if err != nil { return res, err @@ -1004,6 +976,69 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) return res, nil } +// SetDefaultVoucher retrieves the current vouchers +// and sets the first as the default voucher, if no active voucher is set +func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + store := h.userdataStore + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + fmt.Println("Running SetDefaultVoucher") + + // check if the user has an active sym + _, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + + if err != nil { + if db.IsNotFound(err) { + publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + + if err != nil { + return res, nil + } + + // Fetch vouchers from the API using the public key + vouchersResp, err := h.accountService.FetchVouchers(string(publicKey)) + if err != nil { + return res, nil + } + + // Ensure there is at least one voucher + if len(vouchersResp.Result.Holdings) == 0 { + return res, err + } + + // Use only the first voucher + firstVoucher := vouchersResp.Result.Holdings[0] + defaultSym := firstVoucher.TokenSymbol + defaultBal := firstVoucher.Balance + + // set the active symbol + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(defaultSym)) + if err != nil { + return res, err + } + // set the active balance + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(defaultBal)) + if err != nil { + return res, err + } + + return res, nil + } + + fmt.Println("Nothing will happen as the error in not 404") + + return res, err + } + + return res, nil +} + // CheckVouchers retrieves the token holdings from the API using the "PublicKey" and stores // them to gdbm func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { diff --git a/services/registration/main.vis b/services/registration/main.vis index 88f8a42..b809ece 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -1,3 +1,5 @@ +LOAD set_default_voucher 8 +RELOAD set_default_voucher LOAD check_balance 64 RELOAD check_balance LOAD check_vouchers 10 From 859e203a006159da26aa784fa2f87ae4a0772b57 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 15:54:43 +0300 Subject: [PATCH 045/175] removed debug comments --- internal/handlers/ussd/menuhandler.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index af3b99f..c3d1870 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -988,8 +988,6 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by return res, fmt.Errorf("missing session") } - fmt.Println("Running SetDefaultVoucher") - // check if the user has an active sym _, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) @@ -1031,8 +1029,6 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by return res, nil } - fmt.Println("Nothing will happen as the error in not 404") - return res, err } From b2fb9faf6c450d5afe1e79f9a6a6220c4a9b2a6d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 16:17:33 +0300 Subject: [PATCH 046/175] use the active balance to validate the amount --- internal/handlers/ussd/menuhandler.go | 41 +++++---------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index c3d1870..f263e4b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -745,43 +745,16 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + var balanceValue float64 + + // retrieve the active balance + activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) if err != nil { return res, err } - - // retrieve the active symbol - activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) - useActiveSymbol := err == nil && len(activeSym) > 0 - - var balanceValue float64 - if useActiveSymbol { - // If active symbol is set, retrieve its balance - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) - if err != nil { - return res, fmt.Errorf("failed to get active balance: %v", err) - } - balanceValue, err = strconv.ParseFloat(string(activeBal), 64) - if err != nil { - return res, fmt.Errorf("failed to parse active balance: %v", err) - } - } else { - // If no active symbol, use the current balance from the API - balanceStr, err := h.accountService.CheckBalance(string(publicKey)) - if err != nil { - return res, fmt.Errorf("failed to check balance: %v", err) - } - res.Content = balanceStr - - // Parse the balance string - balanceParts := strings.Split(balanceStr, " ") - if len(balanceParts) != 2 { - return res, fmt.Errorf("unexpected balance format: %s", balanceStr) - } - balanceValue, err = strconv.ParseFloat(balanceParts[0], 64) - if err != nil { - return res, fmt.Errorf("failed to parse balance: %v", err) - } + balanceValue, err = strconv.ParseFloat(string(activeBal), 64) + if err != nil { + return res, err } // Extract numeric part from the input amount From f378f154223349108e953f2a2c0edeb76b9c2680 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 16:29:51 +0300 Subject: [PATCH 047/175] updated tests --- internal/handlers/ussd/menuhandler_test.go | 61 ++++------------------ 1 file changed, 9 insertions(+), 52 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index f36814a..901a15a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1415,17 +1415,13 @@ func TestValidateAmount(t *testing.T) { tests := []struct { name string input []byte - publicKey []byte - activeSym []byte activeBal []byte balance string expectedResult resource.Result }{ { - name: "Test with valid amount and active symbol", + name: "Test with valid amount", input: []byte("0.001"), - publicKey: []byte("0xrqeqrequuq"), - activeSym: []byte("CELO"), activeBal: []byte("0.003"), expectedResult: resource.Result{ Content: "0.001", @@ -1434,8 +1430,6 @@ func TestValidateAmount(t *testing.T) { { name: "Test with amount larger than active balance", input: []byte("0.02"), - publicKey: []byte("0xrqeqrequuq"), - activeSym: []byte("CELO"), activeBal: []byte("0.003"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, @@ -1445,37 +1439,18 @@ func TestValidateAmount(t *testing.T) { { name: "Test with invalid amount format", input: []byte("0.02ms"), - publicKey: []byte("0xrqeqrequuq"), balance: "0.003 CELO", expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, Content: "0.02ms", }, }, - { - name: "Test fallback to current balance without active symbol", - input: []byte("0.001"), - publicKey: []byte("0xrqeqrequuq"), - balance: "0.003 CELO", - expectedResult: resource.Result{ - Content: "0.001", - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Mock behavior for public key retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil) - - // Mock behavior for active symbol and balance retrieval (if present) - if len(tt.activeSym) > 0 { - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(tt.activeSym, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) - } else { - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(nil, fmt.Errorf("not found")) - mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil) - } + // Mock behavior for active balance retrieval + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) // Mock behavior for storing the amount (if valid) mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe() @@ -1572,15 +1547,6 @@ func TestCheckBalance(t *testing.T) { expectedResult: resource.Result{Content: "1.5 ETH"}, expectError: false, }, - { - name: "User without active sym", - sessionId: "session123", - publicKey: "0X13242618721", - activeSym: "", - activeBal: "", - expectedResult: resource.Result{Content: "0.003 CELO"}, - expectError: false, - }, } for _, tt := range tests { @@ -1593,21 +1559,12 @@ func TestCheckBalance(t *testing.T) { userdataStore: mockDataStore, accountService: mockAccountService, } - - // Mock calls for public key - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(tt.publicKey), nil) - - if tt.activeSym == "" { - // Mock for user without active sym - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte{}, db.ErrNotFound{}) - mockAccountService.On("CheckBalance", tt.publicKey).Return("0.003 CELO", nil) - } else { - // Mock for user with active sym - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) - } - - res, err := h.CheckBalance(ctx, "check_balance", []byte("123456")) + + // Mock for user with active sym + mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) + mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) + + res, err := h.CheckBalance(ctx, "check_balance", []byte("")) if tt.expectError { assert.Error(t, err) From 1d27a889085336c019fb4b532d9760724fe7ddb6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 16:30:29 +0300 Subject: [PATCH 048/175] add test on validate amount --- internal/handlers/ussd/menuhandler_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 82cca01..0bde51a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1468,6 +1468,7 @@ func TestValidateAmount(t *testing.T) { t.Logf(err.Error()) } flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") + flag_api_error, _ := fm.GetFlag("flag_api_call_error") mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) @@ -1502,7 +1503,8 @@ func TestValidateAmount(t *testing.T) { }, publicKey: []byte("0xrqeqrequuq"), expectedResult: resource.Result{ - Content: "0.001", + Content: "0.001", + FlagReset: []uint32{flag_api_error}, }, }, { @@ -1520,8 +1522,9 @@ func TestValidateAmount(t *testing.T) { }, publicKey: []byte("0xrqeqrequuq"), expectedResult: resource.Result{ - FlagSet: []uint32{flag_invalid_amount}, - Content: "0.02", + FlagSet: []uint32{flag_invalid_amount}, + FlagReset: []uint32{flag_api_error}, + Content: "0.02", }, }, { @@ -1539,8 +1542,9 @@ func TestValidateAmount(t *testing.T) { }, publicKey: []byte("0xrqeqrequuq"), expectedResult: resource.Result{ - FlagSet: []uint32{flag_invalid_amount}, - Content: "0.02ms", + FlagSet: []uint32{flag_invalid_amount}, + FlagReset: []uint32{flag_api_error}, + Content: "0.02ms", }, }, } From 6a36bc43b5cb88a1eac31d074d5402436b8933bd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 16:31:15 +0300 Subject: [PATCH 049/175] add check for api call failure --- internal/handlers/ussd/menuhandler.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 30baf93..6059dd4 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -829,6 +829,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) } flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") store := h.userdataStore publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) @@ -838,10 +839,15 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) balanceRes, err := h.accountService.CheckBalance(string(publicKey)) balanceStr := balanceRes.Result.Balance + if !balanceRes.Ok { + res.FlagSet = append(res.FlagSet, flag_api_error) + return res, nil + } if err != nil { return res, err } res.Content = balanceStr + res.FlagReset = append(res.FlagReset, flag_api_error) // Parse the balance balanceParts := strings.Split(balanceStr, " ") From 0be570ae2df88c7181c50cd643b244f74f793103 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 15 Oct 2024 16:31:31 +0300 Subject: [PATCH 050/175] add check for api call failure --- services/registration/amount.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/amount.vis b/services/registration/amount.vis index b491fab..e87006e 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -5,6 +5,7 @@ MOUT back 0 HALT LOAD validate_amount 64 RELOAD validate_amount +CATCH api_failure flag_api_call_error 1 CATCH invalid_amount flag_invalid_amount 1 INCMP _ 0 LOAD get_recipient 12 From a336856c9b8fc4b028c29cce967d043a609468c3 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 17:02:51 +0300 Subject: [PATCH 051/175] use separate functions to process voucher data --- internal/handlers/ussd/menuhandler.go | 79 ++++++++++++++++----------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f263e4b..8041bf7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1029,15 +1029,8 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - var numberedSymbols []string - var numberedBalances []string - for i, voucher := range vouchersResp.Result.Holdings { - numberedSymbols = append(numberedSymbols, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) - numberedBalances = append(numberedBalances, fmt.Sprintf("%d:%s", i+1, voucher.Balance)) - } - - voucherSymbolList := strings.Join(numberedSymbols, "\n") - voucherBalanceList := strings.Join(numberedBalances, "\n") + // process voucher data + voucherSymbolList, voucherBalanceList := ProcessVouchers(vouchersResp.Result.Holdings) prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) err = prefixdb.Put(ctx, []byte("sym"), []byte(voucherSymbolList)) @@ -1053,6 +1046,26 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } +// ProcessVouchers formats the holdings into symbol and balance lists. +func ProcessVouchers(holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` +}) (string, string) { + var numberedSymbols, numberedBalances []string + + for i, voucher := range holdings { + numberedSymbols = append(numberedSymbols, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) + numberedBalances = append(numberedBalances, fmt.Sprintf("%d:%s", i+1, voucher.Balance)) + } + + voucherSymbolList := strings.Join(numberedSymbols, "\n") + voucherBalanceList := strings.Join(numberedBalances, "\n") + + return voucherSymbolList, voucherBalanceList +} + // GetVoucherList fetches the list of vouchers and formats them func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1074,7 +1087,6 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) // ViewVoucher retrieves the token holding and balance from the subprefixDB func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) @@ -1105,10 +1117,31 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, fmt.Errorf("failed to retrieve voucher balance list: %v", err) } - // Convert the symbol and balance lists from byte arrays to strings - voucherSymbols := string(voucherSymbolList) - voucherBalances := string(voucherBalanceList) + // match the voucher symbol and balance with the input + matchedSymbol, matchedBalance := MatchVoucher(inputStr, string(voucherSymbolList), string(voucherBalanceList)) + // If a match is found, write the temporary sym and balance + if matchedSymbol != "" && matchedBalance != "" { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) + if err != nil { + return res, err + } + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance)) + if err != nil { + return res, err + } + + res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) + res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) + } else { + res.FlagSet = append(res.FlagSet, flag_incorrect_voucher) + } + + return res, nil +} + +// MatchVoucher finds the matching voucher symbol and balance based on the input. +func MatchVoucher(inputStr string, voucherSymbols, voucherBalances string) (string, string) { // Split the lists into slices for processing symbols := strings.Split(voucherSymbols, "\n") balances := strings.Split(voucherBalances, "\n") @@ -1128,29 +1161,13 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r matchedSymbol = voucherSymbol // Ensure there's a corresponding balance if i < len(balances) { - matchedBalance = strings.SplitN(balances[i], ":", 2)[1] // Extract balance after the "x:balance" format + matchedBalance = strings.SplitN(balances[i], ":", 2)[1] } break } } - // If a match is found, write the temporary sym, then return the symbol and balance - if matchedSymbol != "" && matchedBalance != "" { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) - if err != nil { - return res, err - } - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance)) - if err != nil { - return res, err - } - res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) - res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) - } else { - res.FlagSet = append(res.FlagSet, flag_incorrect_voucher) - } - - return res, nil + return matchedSymbol, matchedBalance } // SetVoucher retrieves the temporary sym and balance, From 51122d0fc52bcd7b809216e9702c2e1d4924e212 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 22:31:37 +0300 Subject: [PATCH 052/175] added an env.example file --- .env.example | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3bfd724 --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +#Serve Http +PORT=7123 +HOST=127.0.0.1 + +#PostgreSQL +DB_HOST=localhost +DB_USER=postgres +DB_PASSWORD=strongpass +DB_NAME=urdt_ussd +DB_PORT=5432 +DB_SSLMODE=disable +DB_TIMEZONE=Africa/Nairobi + +#External API Calls +CREATE_ACCOUNT_URL="https://custodial.sarafu.africa/api/account/create" +TRACK_STATUS_URL="https://custodial.sarafu.africa/api/track/" +BALANCE_URL="https://custodial.sarafu.africa/api/account/status/" From 09eac03e10351829f45245ee6316c9b383af84ba Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 23:41:16 +0300 Subject: [PATCH 053/175] use env variables --- .env.example | 6 +++--- cmd/africastalking/main.go | 13 ++++++++++-- cmd/async/main.go | 12 +++++++++-- cmd/http/main.go | 12 +++++++++-- cmd/main.go | 8 ++++++++ config/config.go | 16 ++++++++++----- go.mod | 2 ++ go.sum | 2 ++ initializers/loadEnvVariables.go | 34 ++++++++++++++++++++++++++++++++ 9 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 initializers/loadEnvVariables.go diff --git a/.env.example b/.env.example index 3bfd724..97d3317 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,6 @@ DB_SSLMODE=disable DB_TIMEZONE=Africa/Nairobi #External API Calls -CREATE_ACCOUNT_URL="https://custodial.sarafu.africa/api/account/create" -TRACK_STATUS_URL="https://custodial.sarafu.africa/api/track/" -BALANCE_URL="https://custodial.sarafu.africa/api/account/status/" +CREATE_ACCOUNT_URL=https://custodial.sarafu.africa/api/account/create +TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ +BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index c24c4b1..3f1c723 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -16,6 +16,8 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" @@ -26,6 +28,11 @@ var ( scriptDir = path.Join("services", "registration") ) +func init() { + fmt.Println("Running init") + initializers.LoadEnvVariables() +} + type atRequestParser struct{} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { @@ -65,6 +72,8 @@ func (arp *atRequestParser) GetInput(rq any) ([]byte, error) { } func main() { + config.LoadConfig() + var dbDir string var resourceDir string var size uint @@ -75,8 +84,8 @@ func main() { flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") - flag.StringVar(&host, "h", "127.0.0.1", "http host") - flag.UintVar(&port, "p", 7123, "http port") + flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") + flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) diff --git a/cmd/async/main.go b/cmd/async/main.go index 09236fd..db58857 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -13,6 +13,8 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -22,6 +24,10 @@ var ( scriptDir = path.Join("services", "registration") ) +func init() { + initializers.LoadEnvVariables() +} + type asyncRequestParser struct { sessionId string input []byte @@ -36,6 +42,8 @@ func (p *asyncRequestParser) GetInput(r any) ([]byte, error) { } func main() { + config.LoadConfig() + var sessionId string var dbDir string var resourceDir string @@ -48,8 +56,8 @@ func main() { flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") - flag.StringVar(&host, "h", "127.0.0.1", "http host") - flag.UintVar(&port, "p", 7123, "http port") + flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") + flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId) diff --git a/cmd/http/main.go b/cmd/http/main.go index 6b868ed..166ab75 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -15,6 +15,8 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" @@ -25,7 +27,13 @@ var ( scriptDir = path.Join("services", "registration") ) +func init() { + initializers.LoadEnvVariables() +} + func main() { + config.LoadConfig() + var dbDir string var resourceDir string var size uint @@ -36,8 +44,8 @@ func main() { flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") - flag.StringVar(&host, "h", "127.0.0.1", "http host") - flag.UintVar(&port, "p", 7123, "http port") + flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") + flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) diff --git a/cmd/main.go b/cmd/main.go index 9db5e0a..e42306a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,6 +10,8 @@ import ( "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -19,7 +21,13 @@ var ( scriptDir = path.Join("services", "registration") ) +func init() { + initializers.LoadEnvVariables() +} + func main() { + config.LoadConfig() + var dbDir string var size uint var sessionId string diff --git a/config/config.go b/config/config.go index 0571503..dba0da7 100644 --- a/config/config.go +++ b/config/config.go @@ -1,10 +1,16 @@ package config +import "git.grassecon.net/urdt/ussd/initializers" - -const ( - CreateAccountURL = "https://custodial.sarafu.africa/api/account/create" - TrackStatusURL = "https://custodial.sarafu.africa/api/track/" - BalanceURL = "https://custodial.sarafu.africa/api/account/status/" +var ( + CreateAccountURL string + TrackStatusURL string + BalanceURL string ) +// LoadConfig initializes the configuration values after environment variables are loaded. +func LoadConfig() { + CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "https://custodial.sarafu.africa/api/account/create") + TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") + BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") +} diff --git a/go.mod b/go.mod index c4c5167..1e8f630 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) +require github.com/joho/godotenv v1.5.1 // indirect + require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/repr v0.2.0 // indirect diff --git a/go.sum b/go.sum index ed5636f..34ee911 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qf github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= diff --git a/initializers/loadEnvVariables.go b/initializers/loadEnvVariables.go new file mode 100644 index 0000000..4ea5980 --- /dev/null +++ b/initializers/loadEnvVariables.go @@ -0,0 +1,34 @@ +package initializers + +import ( + "log" + "os" + "strconv" + + "github.com/joho/godotenv" +) + +func LoadEnvVariables() { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } +} + +// Helper to get environment variables with a default fallback +func GetEnv(key, defaultVal string) string { + if value, exists := os.LookupEnv(key); exists { + return value + } + return defaultVal +} + +// Helper to safely convert environment variables to uint +func GetEnvUint(key string, defaultVal uint) uint { + if value, exists := os.LookupEnv(key); exists { + if parsed, err := strconv.Atoi(value); err == nil && parsed >= 0 { + return uint(parsed) + } + } + return defaultVal +} From d8db8df64364ae7b09f12c18fdcf353942cc8f09 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 15 Oct 2024 23:42:39 +0300 Subject: [PATCH 054/175] use go-vise v0.2.0 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1e8f630..dcd1d13 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.22.6 require ( - git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb + git.defalsify.org/vise.git v0.2.0 github.com/alecthomas/assert/v2 v2.2.2 github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 diff --git a/go.sum b/go.sum index 34ee911..0f7275c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihcwMjDKzvUFC6t2zGNb7MDW+l/ACGlSAN1N8Y= git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= +git.defalsify.org/vise.git v0.2.0 h1:X2ZgiGRq4C+9qOlDMP0b/oE5QHjVQNT4aEFZB88ST0Q= +git.defalsify.org/vise.git v0.2.0/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= From f73b7a8b04595cabbff4944e0b13fe57842f38f4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 16 Oct 2024 22:40:40 +0300 Subject: [PATCH 055/175] setup api responses --- internal/handlers/server/api.go | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 internal/handlers/server/api.go diff --git a/internal/handlers/server/api.go b/internal/handlers/server/api.go new file mode 100644 index 0000000..646458c --- /dev/null +++ b/internal/handlers/server/api.go @@ -0,0 +1,60 @@ +package server + +type ( + OKResponse struct { + Ok bool `json:"ok"` + Description string `json:"description"` + Result map[string]any `json:"result"` + } + + ErrResponse struct { + Ok bool `json:"ok"` + Description string `json:"description"` + ErrCode string `json:"errorCode"` + } + + TransferRequest struct { + From string `json:"from" validate:"required,eth_addr_checksum"` + To string `json:"to" validate:"required,eth_addr_checksum"` + TokenAddress string `json:"tokenAddress" validate:"required,eth_addr_checksum"` + Amount string `json:"amount" validate:"required,number,gt=0"` + } + + PoolSwapRequest struct { + From string `json:"from" validate:"required,eth_addr_checksum"` + FromTokenAddress string `json:"fromTokenAddress" validate:"required,eth_addr_checksum"` + ToTokenAddress string `json:"toTokenAddress" validate:"required,eth_addr_checksum"` + PoolAddress string `json:"poolAddress" validate:"required,eth_addr_checksum"` + Amount string `json:"amount" validate:"required,number,gt=0"` + } + + PoolDepositRequest struct { + From string `json:"from" validate:"required,eth_addr_checksum"` + TokenAddress string `json:"tokenAddress" validate:"required,eth_addr_checksum"` + PoolAddress string `json:"poolAddress" validate:"required,eth_addr_checksum"` + Amount string `json:"amount" validate:"required,number,gt=0"` + } + + AccountAddressParam struct { + Address string `param:"address" validate:"required,eth_addr_checksum"` + } + + TrackingIDParam struct { + TrackingID string `param:"trackingId" validate:"required,uuid"` + } + + OTXByAccountRequest struct { + Address string `param:"address" validate:"required,eth_addr_checksum"` + PerPage int `query:"perPage" validate:"required,number,gt=0"` + Cursor int `query:"cursor" validate:"number"` + Next bool `query:"next"` + } +) + +const ( + ErrCodeInternalServerError = "E01" + ErrCodeInvalidJSON = "E02" + ErrCodeInvalidAPIKey = "E03" + ErrCodeValidationFailed = "E04" + ErrCodeAccountNotExists = "E05" +) From 4e840ac17c2b0a5c2600dc63463d7010c9bff6f4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:30:36 +0300 Subject: [PATCH 056/175] add uuid --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index c4c5167..1f67f34 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/gofrs/uuid v4.4.0+incompatible github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect diff --git a/go.sum b/go.sum index ed5636f..0eccb60 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= From 8d047ebe05939e4bac975ed9f96241aa46028276 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:30:58 +0300 Subject: [PATCH 057/175] define universal group driver --- driver/groupdriver.go | 111 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 driver/groupdriver.go diff --git a/driver/groupdriver.go b/driver/groupdriver.go new file mode 100644 index 0000000..68cb7e3 --- /dev/null +++ b/driver/groupdriver.go @@ -0,0 +1,111 @@ +package driver + +import ( + "encoding/json" + "log" + "os" + "regexp" +) + +type Step struct { + Input string `json:"input"` + ExpectedContent string `json:"expectedContent"` +} + +func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { + pattern := regexp.QuoteMeta(s.ExpectedContent) + re, err := regexp.Compile(pattern) + if err != nil { + return false, err + } + if re.Match([]byte(content)) { + return true, nil + } + return false, nil +} + +// Group represents a group of steps +type Group struct { + Name string `json:"name"` + Steps []Step `json:"steps"` +} + +type TestCase struct { + Name string + Input string + ExpectedContent string +} + +func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) { + pattern := regexp.QuoteMeta(s.ExpectedContent) + re, err := regexp.Compile(pattern) + if err != nil { + return false, err + } + // Check if the content matches the regex pattern + if re.Match(content) { + return true, nil + } + return false, nil +} + +// DataGroup represents the overall structure of the JSON. +type DataGroup struct { + Groups []Group `json:"groups"` +} + +type Session struct { + Name string `json:"name"` + Groups []Group `json:"groups"` +} + +func ReadData() []Session { + data, err := os.ReadFile("test_setup.json") + if err != nil { + log.Fatalf("Failed to read file: %v", err) + } + // Unmarshal JSON data + var sessions []Session + err = json.Unmarshal(data, &sessions) + if err != nil { + log.Fatalf("Failed to unmarshal JSON: %v", err) + } + + return sessions +} + +func FilterGroupsByName(groups []Group, name string) []Group { + var filteredGroups []Group + for _, group := range groups { + if group.Name == name { + filteredGroups = append(filteredGroups, group) + } + } + return filteredGroups +} + +func LoadTestGroups(filePath string) (DataGroup, error) { + var sessionsData DataGroup + data, err := os.ReadFile(filePath) + if err != nil { + return sessionsData, err + } + err = json.Unmarshal(data, &sessionsData) + return sessionsData, err +} + +func CreateTestCases(group DataGroup) []TestCase { + var tests []TestCase + for _, group := range group.Groups { + for _, step := range group.Steps { + // Create a test case for each group + tests = append(tests, TestCase{ + Name: group.Name, + Input: step.Input, + ExpectedContent: step.ExpectedContent, + }) + } + } + + return tests +} From a51f739d06fff5bd92e1026b43de825fddfbee75 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:31:33 +0300 Subject: [PATCH 058/175] pass account service as a param --- internal/handlers/handlerservice.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 561b76a..708139b 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -6,6 +6,7 @@ import ( "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" ) @@ -52,8 +53,8 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } -func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore) +func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) if err != nil { return nil, err } From 54cc33c8197ef6e42dc2d7ac4c16caac800e746b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:32:31 +0300 Subject: [PATCH 059/175] Delete connstr in threadgdbm global channel map on close --- internal/storage/gdbm.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm.go index eb959cf..49de570 100644 --- a/internal/storage/gdbm.go +++ b/internal/storage/gdbm.go @@ -109,6 +109,7 @@ func(tdb *ThreadGdbmDb) Get(ctx context.Context, key []byte) ([]byte, error) { func(tdb *ThreadGdbmDb) Close() error { tdb.reserve() close(dbC[tdb.connStr]) + delete(dbC, tdb.connStr) err := tdb.db.Close() tdb.db = nil return err From 127219f5103e6f1e5094541bd0d7cbdcafc819f6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:33:09 +0300 Subject: [PATCH 060/175] define build for running online and offline tests --- internal/testutil/offlinetest.go | 11 +++++++++++ internal/testutil/onlinetest.go | 9 +++++++++ 2 files changed, 20 insertions(+) create mode 100644 internal/testutil/offlinetest.go create mode 100644 internal/testutil/onlinetest.go diff --git a/internal/testutil/offlinetest.go b/internal/testutil/offlinetest.go new file mode 100644 index 0000000..476ade3 --- /dev/null +++ b/internal/testutil/offlinetest.go @@ -0,0 +1,11 @@ +// +build !online + +package testutil + +import ( + "git.grassecon.net/urdt/ussd/internal/handlers/server" +) + +var ( + AccountService server.AccountServiceInterface = &server.TestAccountService{} +) diff --git a/internal/testutil/onlinetest.go b/internal/testutil/onlinetest.go new file mode 100644 index 0000000..ddb5cf0 --- /dev/null +++ b/internal/testutil/onlinetest.go @@ -0,0 +1,9 @@ +// +build online + +package testutil + +import "git.grassecon.net/urdt/ussd/internal/handlers/server" + +var ( + AccountService server.AccountServiceInterface +) From f81e3508ca73a919f828ec5db1bc9f5084beb635 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:33:38 +0300 Subject: [PATCH 061/175] define engine to run tests --- internal/testutil/TestEngine.go | 121 ++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 internal/testutil/TestEngine.go diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go new file mode 100644 index 0000000..2432a3f --- /dev/null +++ b/internal/testutil/TestEngine.go @@ -0,0 +1,121 @@ +package testutil + +import ( + "context" + "fmt" + "os" + "path" + "time" + + "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/handlers/server" + "git.grassecon.net/urdt/ussd/internal/storage" + testdataloader "github.com/peteole/testdata-loader" +) + +var ( + baseDir = testdataloader.GetBasePath() + logg = logging.NewVanilla() + scriptDir = path.Join(baseDir, "services", "registration") +) + +func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { + ctx := context.Background() + ctx = context.WithValue(ctx, "SessionId", sessionId) + pfp := path.Join(scriptDir, "pp.csv") + + var eventChannel = make(chan bool) + + cfg := engine.Config{ + Root: "root", + SessionId: sessionId, + OutputSize: uint32(160), + FlagCount: uint32(128), + } + + dbDir := ".test_state" + resourceDir := scriptDir + menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) + + err := menuStorageService.EnsureDbDir() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + rs, err := menuStorageService.GetResource(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + pe, err := menuStorageService.GetPersister(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + userDataStore, err := menuStorageService.GetUserdataDb(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + dbResource, ok := rs.(*resource.DbResource) + if !ok { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs.SetDataStore(&userDataStore) + lhs.SetPersister(pe) + + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + if AccountService == nil { + AccountService = &server.AccountService{} + } + + switch AccountService.(type) { + case *server.TestAccountService: + go func() { + eventChannel <- false + }() + case *server.AccountService: + go func() { + time.Sleep(5 * time.Second) // Wait for 5 seconds + eventChannel <- true + }() + default: + panic("Unknown account service type") + } + + hl, err := lhs.GetHandler(AccountService) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + en := lhs.GetEngine() + en = en.WithFirst(hl.Init) + cleanFn := func() { + err := en.Finish() + if err != nil { + logg.Errorf(err.Error()) + } + + err = menuStorageService.Close() + if err != nil { + logg.Errorf(err.Error()) + } + logg.Infof("testengine storage closed") + } + return en, cleanFn, eventChannel +} From a27c1790b8230b41e0301a3bebc86b1a9271567e Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:34:17 +0300 Subject: [PATCH 062/175] setup test data file --- menutraversal_test/group_test.json | 460 +++++++++++++++++++++++++++++ menutraversal_test/test_setup.json | 153 ++++++++++ 2 files changed, 613 insertions(+) create mode 100644 menutraversal_test/group_test.json create mode 100644 menutraversal_test/test_setup.json diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json new file mode 100644 index 0000000..203ff08 --- /dev/null +++ b/menutraversal_test/group_test.json @@ -0,0 +1,460 @@ +{ + "groups": [ + { + "name": "my_account_change_pin", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "5", + "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n3:Guard my PIN\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your old PIN\n\n0:Back" + }, + { + "input": "1234", + "expectedContent": "Enter a new four number PIN:\n\n0:Back" + }, + { + "input": "1234", + "expectedContent": "Confirm your new PIN:\n\n0:Back" + }, + { + "input": "1234", + "expectedContent": "Your PIN change request has been successful\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_language_change", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "2", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1235", + "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Select language:\n0:english\n1:kiswahili" + }, + { + "input": "0", + "expectedContent": "Your language change request was successful.\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_check_my_balance", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "3", + "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" + }, + { + "input": "1", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1235", + "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Your balance is 0.003 CELO\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" + + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_check_community_balance", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "3", + "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" + }, + { + "input": "2", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1235", + "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Your community balance is 0.003 CELO\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" + + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_edit_firstname", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your first names:\n0:Back" + }, + { + "input": "foo", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_edit_familyname", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "2", + "expectedContent": "Enter family name:\n0:Back" + }, + { + "input": "bar", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + + ] + }, + { + "name": "menu_my_account_edit_gender", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "3", + "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" + }, + { + "input": "1", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_edit_yob", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "4", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1945", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_edit_location", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "5", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_edit_offerings", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "6", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + }, + { + "name": "menu_my_account_view_profile", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "7", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "0", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + + + + + + diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json new file mode 100644 index 0000000..56c0278 --- /dev/null +++ b/menutraversal_test/test_setup.json @@ -0,0 +1,153 @@ +[ + { + "name": "session one", + "groups": [ + { + "name": "account_creation_successful", + "steps": [ + { + "input": "", + "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:english\n1:kiswahili" + }, + { + "input": "0", + "expectedContent": "Do you agree to terms and conditions?\n0:yes\n1:no" + }, + { + "input": "0", + "expectedContent": "Please enter a new four number PIN for your account:\n0:Exit" + }, + { + "input": "1234", + "expectedContent": "Enter your four number PIN again:" + }, + { + "input": "1111", + "expectedContent": "The PIN is not a match. Try again\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Enter your four number PIN again:" + }, + { + "input": "1234", + "expectedContent": "Your account is being created...Thank you for using Sarafu. Goodbye!" + } + ] + }, + { + "name": "account_creation_reject_terms", + "steps": [ + { + "input": "", + "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:english\n1:kiswahili" + }, + { + "input": "0", + "expectedContent": "Do you agree to terms and conditions?\n0:yes\n1:no" + }, + { + "input": "1", + "expectedContent": "Thank you for using Sarafu. Goodbye!" + } + ] + }, + { + "name": "send_with_invalid_inputs", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Enter recipient's phone number:\n0:Back" + }, + { + "input": "000", + "expectedContent": "000 is not registered or invalid, please try again:\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Enter recipient's phone number:\n0:Back" + }, + { + "input": "065656", + "expectedContent": "Maximum amount: 0.003 CELO\nEnter amount:\n0:Back" + }, + { + "input": "0.1", + "expectedContent": "Amount 0.1 is invalid, please try again:\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "Maximum amount: 0.003 CELO\nEnter amount:\n0:Back" + }, + { + "input": "0.001", + "expectedContent": "065656 will receive 0.001 from {public_key}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" + }, + { + "input": "1222", + "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + }, + { + "input": "1", + "expectedContent": "065656 will receive 0.001 from {public_key}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" + }, + { + "input": "1234", + "expectedContent": "Your request has been sent. 065656 will receive 0.001 from {public_key}." + } + ] + }, + { + "name": "main_menu_help", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "4", + "expectedContent": "For more help,please call: 0757628885" + } + ] + }, + { + "name": "main_menu_quit", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "9", + "expectedContent": "Thank you for using Sarafu. Goodbye!" + } + ] + }, + { + "name": "menu_my_account_my_address", + "steps": [ + { + "input": "", + "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "6", + "expectedContent": "Address: {public_key}\n9:Quit" + }, + { + "input": "9", + "expectedContent": "Thank you for using Sarafu. Goodbye!" + } + ] + } + ] + } +] \ No newline at end of file From 383e64776db02ce3298a785cc08a277bf2eaa5c3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:35:44 +0300 Subject: [PATCH 063/175] chore: change pin to PIN --- services/registration/new_pin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/new_pin b/services/registration/new_pin index bae2814..65d8ed3 100644 --- a/services/registration/new_pin +++ b/services/registration/new_pin @@ -1 +1 @@ -Enter a new four number pin +Enter a new four number PIN: From 0a7389a71d06bcd515f913b55f811409ad657b75 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:36:02 +0300 Subject: [PATCH 064/175] explicitly reset authorize flag --- services/registration/edit_profile.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index 9d45ec9..277f330 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -1,4 +1,5 @@ LOAD reset_account_authorized 16 +RELOAD reset_account_authorized LOAD reset_allow_update 0 RELOAD reset_allow_update MOUT edit_name 1 From 4416c008fcfdff5a07c49ccb217bd7fde2ecd295 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:36:24 +0300 Subject: [PATCH 065/175] add menu traversal tests --- menutraversal_test/menu_traversal_test.go | 278 ++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 menutraversal_test/menu_traversal_test.go diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go new file mode 100644 index 0000000..5eb1ef8 --- /dev/null +++ b/menutraversal_test/menu_traversal_test.go @@ -0,0 +1,278 @@ +package menutraversaltest + +import ( + "bytes" + "context" + "log" + "math/rand" + "os" + "regexp" + "testing" + + "git.grassecon.net/urdt/ussd/driver" + "git.grassecon.net/urdt/ussd/internal/testutil" + "github.com/gofrs/uuid" +) + +var ( + testData = driver.ReadData() + testStore = ".test_state" + groupTestFile = "group_test.json" + sessionID string + src = rand.NewSource(42) + g = rand.New(src) +) + +func GenerateSessionId() string { + uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g)) + v, err := uu.NewV4() + if err != nil { + panic(err) + } + return v.String() +} + +// Extract the public key from the engine response +func extractPublicKey(response []byte) string { + // Regex pattern to match the public key starting with 0x and 40 characters + re := regexp.MustCompile(`0x[a-fA-F0-9]{40}`) + match := re.Find(response) + if match != nil { + return string(match) + } + return "" +} + +func TestMain(m *testing.M) { + sessionID = GenerateSessionId() + defer func() { + if err := os.RemoveAll(testStore); err != nil { + log.Fatalf("Failed to delete state store %s: %v", testStore, err) + } + }() + m.Run() +} + +func TestAccountCreationSuccessful(t *testing.T) { + en, fn, eventChannel := testutil.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "account_creation_successful") + for _, group := range groups { + for _, step := range group.Steps { + cont, err := en.Exec(ctx, []byte(step.Input)) + if err != nil { + t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) + } + if !cont { + break + } + w := bytes.NewBuffer(nil) + _, err = en.Flush(ctx, w) + if err != nil { + t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) + } + b := w.Bytes() + match, err := step.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } + <-eventChannel + +} + +func TestAccountRegistrationRejectTerms(t *testing.T) { + // Generate a new UUID for this edge case test + uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g)) + v, err := uu.NewV4() + if err != nil { + t.Fail() + } + edgeCaseSessionID := v.String() + en, fn, _ := testutil.TestEngine(edgeCaseSessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "account_creation_reject_terms") + for _, group := range groups { + for _, step := range group.Steps { + cont, err := en.Exec(ctx, []byte(step.Input)) + if err != nil { + t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) + return + } + if !cont { + break + } + w := bytes.NewBuffer(nil) + if _, err := en.Flush(ctx, w); err != nil { + t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) + } + + b := w.Bytes() + match, err := step.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMainMenuHelp(t *testing.T) { + en, fn, _ := testutil.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "main_menu_help") + for _, group := range groups { + for _, step := range group.Steps { + cont, err := en.Exec(ctx, []byte(step.Input)) + if err != nil { + t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) + return + } + if !cont { + break + } + w := bytes.NewBuffer(nil) + if _, err := en.Flush(ctx, w); err != nil { + t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) + } + + b := w.Bytes() + match, err := step.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMainMenuQuit(t *testing.T) { + en, fn, _ := testutil.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "main_menu_quit") + for _, group := range groups { + for _, step := range group.Steps { + cont, err := en.Exec(ctx, []byte(step.Input)) + if err != nil { + t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) + return + } + if !cont { + break + } + w := bytes.NewBuffer(nil) + if _, err := en.Flush(ctx, w); err != nil { + t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) + } + + b := w.Bytes() + match, err := step.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMyAccount_MyAddress(t *testing.T) { + en, fn, _ := testutil.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "menu_my_account_my_address") + for _, group := range groups { + for index, step := range group.Steps { + t.Logf("step %v with input %v", index, step.Input) + cont, err := en.Exec(ctx, []byte(step.Input)) + if err != nil { + t.Errorf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) + return + } + if !cont { + break + } + w := bytes.NewBuffer(nil) + if _, err := en.Flush(ctx, w); err != nil { + t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) + } + b := w.Bytes() + + publicKey := extractPublicKey(b) + expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) + step.ExpectedContent = string(expectedContent) + match, err := step.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b) + } + } + } + } +} + +func TestGroups(t *testing.T) { + groups, err := driver.LoadTestGroups(groupTestFile) + if err != nil { + log.Fatalf("Failed to load test groups: %v", err) + } + en, fn, _ := testutil.TestEngine(sessionID) + defer fn() + ctx := context.Background() + // Create test cases from loaded groups + tests := driver.CreateTestCases(groups) + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + cont, err := en.Exec(ctx, []byte(tt.Input)) + if err != nil { + t.Errorf("Test case '%s' failed at input '%s': %v", tt.Name, tt.Input, err) + return + } + if !cont { + return + } + w := bytes.NewBuffer(nil) + if _, err := en.Flush(ctx, w); err != nil { + t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err) + } + b := w.Bytes() + match, err := tt.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", tt.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b) + } + + }) + } +} From 667a21d9507d110881edb230e1293351ac4e90e3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:37:15 +0300 Subject: [PATCH 066/175] pass account service as a param --- internal/handlers/ussd/menuhandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 6059dd4..36d1ad5 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -61,7 +61,7 @@ type Handlers struct { accountService server.AccountServiceInterface } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } @@ -71,7 +71,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db) (*Handlers, erro h := &Handlers{ userdataStore: userDb, flagManager: appFlags, - accountService: &server.AccountService{}, + accountService: accountService, } return h, nil } From 1aeb18379c4b078a07ecfb307aae7f164ad0eb3a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 17 Oct 2024 12:46:20 +0300 Subject: [PATCH 067/175] added a db flag and Database to the context --- cmd/africastalking/main.go | 3 +++ cmd/async/main.go | 3 +++ cmd/http/main.go | 3 +++ cmd/main.go | 3 +++ 4 files changed, 12 insertions(+) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 3f1c723..56a6c81 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -77,11 +77,13 @@ func main() { var dbDir string var resourceDir string var size uint + var database string var engineDebug bool var host string var port uint flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") + flag.StringVar(&database, "db", "gdbm", "database to be used") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") @@ -91,6 +93,7 @@ func main() { logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) ctx := context.Background() + ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ diff --git a/cmd/async/main.go b/cmd/async/main.go index db58857..e815c96 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -48,12 +48,14 @@ func main() { var dbDir string var resourceDir string var size uint + var database string var engineDebug bool var host string var port uint flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") + flag.StringVar(&database, "db", "gdbm", "database to be used") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") @@ -63,6 +65,7 @@ func main() { logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId) ctx := context.Background() + ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ diff --git a/cmd/http/main.go b/cmd/http/main.go index 166ab75..774eb87 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -37,11 +37,13 @@ func main() { var dbDir string var resourceDir string var size uint + var database string var engineDebug bool var host string var port uint flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") + flag.StringVar(&database, "db", "gdbm", "database to be used") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") @@ -51,6 +53,7 @@ func main() { logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) ctx := context.Background() + ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ diff --git a/cmd/main.go b/cmd/main.go index e42306a..be2bea3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -31,8 +31,10 @@ func main() { var dbDir string var size uint var sessionId string + var database string var engineDebug bool flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") + flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") @@ -42,6 +44,7 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ From bfa6eac4c29b2c28d2db0e2f7efc92cd9a238892 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:47:44 +0300 Subject: [PATCH 068/175] define a test account service --- internal/handlers/server/accountservice.go | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 1ddf7e7..5b71e6f 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io" "net/http" + "time" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" @@ -18,6 +19,9 @@ type AccountServiceInterface interface { type AccountService struct { } +type TestAccountService struct { +} + // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. // // Parameters: @@ -92,3 +96,58 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { } return &accountResp, nil } + +func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { + return &models.AccountResponse{ + Ok: true, + Result: struct { + CustodialId json.Number `json:"custodialId"` + PublicKey string `json:"publicKey"` + TrackingId string `json:"trackingId"` + }{ + CustodialId: json.Number("182"), + PublicKey: "0x48ADca309b5085852207FAaf2816eD72B52F527C", + TrackingId: "28ebe84d-b925-472c-87ae-bbdfa1fb97be", + }, + }, nil +} + +func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { + + balanceResponse := &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + } + + return balanceResponse, nil +} + +func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { + trackResponse := &models.TrackStatusResponse{ + Ok: true, + Result: struct { + Transaction struct { + CreatedAt time.Time "json:\"createdAt\"" + Status string "json:\"status\"" + TransferValue json.Number "json:\"transferValue\"" + TxHash string "json:\"txHash\"" + TxType string "json:\"txType\"" + } + }{ + Transaction: models.Transaction{ + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", + }, + }, + } + return trackResponse, nil +} From 4968cdff37cc8a9ab5e3f20db99fce6c684e25e6 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 17 Oct 2024 12:47:57 +0300 Subject: [PATCH 069/175] switch to postgres once the flag is set --- internal/storage/postgres.go | 115 +++++++++++++++++++++++++++++ internal/storage/storageservice.go | 53 ++++++++++--- 2 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 internal/storage/postgres.go diff --git a/internal/storage/postgres.go b/internal/storage/postgres.go new file mode 100644 index 0000000..ed4a711 --- /dev/null +++ b/internal/storage/postgres.go @@ -0,0 +1,115 @@ +package storage + +import ( + "context" + + "git.defalsify.org/vise.git/db" + postgres "git.defalsify.org/vise.git/db/postgres" + "git.defalsify.org/vise.git/lang" +) + +var ( + pdbC map[string]chan db.Db +) + +type ThreadPostgresDb struct { + db db.Db + connStr string +} + +func NewThreadPostgresDb() *ThreadPostgresDb { + if pdbC == nil { + pdbC = make(map[string]chan db.Db) + } + return &ThreadPostgresDb{} +} + +func (tpdb *ThreadPostgresDb) Connect(ctx context.Context, connStr string) error { + var ok bool + _, ok = pdbC[connStr] + if ok { + logg.WarnCtxf(ctx, "already registered thread postgres, skipping", "connStr", connStr) + return nil + } + postgresdb := postgres.NewPgDb().WithSchema("public") + err := postgresdb.Connect(ctx, connStr) + if err != nil { + return err + } + pdbC[connStr] = make(chan db.Db, 1) + pdbC[connStr] <- postgresdb + tpdb.connStr = connStr + return nil +} + +func (tpdb *ThreadPostgresDb) reserve() { + if tpdb.db == nil { + tpdb.db = <-pdbC[tpdb.connStr] + } +} + +func (tpdb *ThreadPostgresDb) release() { + if tpdb.db == nil { + return + } + pdbC[tpdb.connStr] <- tpdb.db + tpdb.db = nil +} + +func (tpdb *ThreadPostgresDb) SetPrefix(pfx uint8) { + tpdb.reserve() + tpdb.db.SetPrefix(pfx) +} + +func (tpdb *ThreadPostgresDb) SetSession(sessionId string) { + tpdb.reserve() + tpdb.db.SetSession(sessionId) +} + +func (tpdb *ThreadPostgresDb) SetLanguage(lng *lang.Language) { + tpdb.reserve() + tpdb.db.SetLanguage(lng) +} + +func (tpdb *ThreadPostgresDb) Safe() bool { + tpdb.reserve() + v := tpdb.db.Safe() + tpdb.release() + return v +} + +func (tpdb *ThreadPostgresDb) Prefix() uint8 { + tpdb.reserve() + v := tpdb.db.Prefix() + tpdb.release() + return v +} + +func (tpdb *ThreadPostgresDb) SetLock(typ uint8, locked bool) error { + tpdb.reserve() + err := tpdb.db.SetLock(typ, locked) + tpdb.release() + return err +} + +func (tpdb *ThreadPostgresDb) Put(ctx context.Context, key []byte, val []byte) error { + tpdb.reserve() + err := tpdb.db.Put(ctx, key, val) + tpdb.release() + return err +} + +func (tpdb *ThreadPostgresDb) Get(ctx context.Context, key []byte) ([]byte, error) { + tpdb.reserve() + v, err := tpdb.db.Get(ctx, key) + tpdb.release() + return v, err +} + +func (tpdb *ThreadPostgresDb) Close() error { + tpdb.reserve() + close(pdbC[tpdb.connStr]) + err := tpdb.db.Close() + tpdb.db = nil + return err +} diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index 07bccd6..d99e0ca 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -8,14 +8,15 @@ import ( "git.defalsify.org/vise.git/db" fsdb "git.defalsify.org/vise.git/db/fs" + "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" - "git.defalsify.org/vise.git/logging" + "git.grassecon.net/urdt/ussd/initializers" ) var ( logg = logging.NewVanilla().WithDomain("storage") -) +) type StorageService interface { GetPersister(ctx context.Context) (*persist.Persister, error) @@ -24,17 +25,30 @@ type StorageService interface { EnsureDbDir() error } -type MenuStorageService struct{ - dbDir string - resourceDir string +type MenuStorageService struct { + dbDir string + resourceDir string resourceStore db.Db - stateStore db.Db + stateStore db.Db userDataStore db.Db } +func buildConnStr() string { + host := initializers.GetEnv("DB_HOST", "localhost") + user := initializers.GetEnv("DB_USER", "postgres") + password := initializers.GetEnv("DB_PASSWORD", "") + dbName := initializers.GetEnv("DB_NAME", "") + port := initializers.GetEnv("DB_PORT", "5432") + + return fmt.Sprintf( + "postgres://%s:%s@%s:%s/%s", + user, password, host, port, dbName, + ) +} + func NewMenuStorageService(dbDir string, resourceDir string) *MenuStorageService { return &MenuStorageService{ - dbDir: dbDir, + dbDir: dbDir, resourceDir: resourceDir, } } @@ -52,12 +66,27 @@ func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persis } func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { - ms.userDataStore = NewThreadGdbmDb() - storeFile := path.Join(ms.dbDir, "userdata.gdbm") - err := ms.userDataStore.Connect(ctx, storeFile) - if err != nil { - return nil, err + database, ok := ctx.Value("Database").(string) + if !ok { + fmt.Println("The database is not set") } + + if database == "postgres" { + ms.userDataStore = NewThreadPostgresDb() + connStr := buildConnStr() + err := ms.userDataStore.Connect(ctx, connStr) + if err != nil { + return nil, err + } + } else { + ms.userDataStore = NewThreadGdbmDb() + storeFile := path.Join(ms.dbDir, "userdata.gdbm") + err := ms.userDataStore.Connect(ctx, storeFile) + if err != nil { + return nil, err + } + } + return ms.userDataStore, nil } From d181c3494618c3c3ffce6336ef8ada05b487ebb9 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:49:28 +0300 Subject: [PATCH 070/175] update handler and move transaction struct to models --- internal/handlers/ussd/menuhandler_test.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 0bde51a..38c468c 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -15,6 +15,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" + "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/mocks" "git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/utils" @@ -28,22 +29,15 @@ var ( flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") ) -type Transaction struct { - CreatedAt time.Time `json:"createdAt"` - Status string `json:"status"` - TransferValue json.Number `json:"transferValue"` - TxHash string `json:"txHash"` - TxType string `json:"txType"` -} - func TestNewHandlers(t *testing.T) { fm, err := NewFlagManager(flagsPath) + accountService := server.TestAccountService{} if err != nil { t.Logf(err.Error()) } t.Run("Valid UserDataStore", func(t *testing.T) { mockStore := &mocks.MockUserDataStore{} - handlers, err := NewHandlers(fm.parser, mockStore) + handlers, err := NewHandlers(fm.parser, mockStore, &accountService) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -59,7 +53,7 @@ func TestNewHandlers(t *testing.T) { t.Run("Nil UserDataStore", func(t *testing.T) { appFlags := &asm.FlagParser{} - handlers, err := NewHandlers(appFlags, nil) + handlers, err := NewHandlers(appFlags, nil, &accountService) if err == nil { t.Fatal("expected an error, got none") @@ -1089,7 +1083,7 @@ func TestCheckAccountStatus(t *testing.T) { TxType string "json:\"txType\"" } }{ - Transaction: Transaction{ + Transaction: models.Transaction{ CreatedAt: time.Now(), Status: "SUCCESS", TransferValue: json.Number("0.5"), @@ -1127,7 +1121,7 @@ func TestCheckAccountStatus(t *testing.T) { TxType string "json:\"txType\"" } }{ - Transaction: Transaction{ + Transaction: models.Transaction{ CreatedAt: time.Now(), Status: "IN_NETWORK", TransferValue: json.Number("0.5"), From 4a599b902dd4f2fefc4880555db301073d1410b0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:49:51 +0300 Subject: [PATCH 071/175] define transaction --- internal/models/trackstatusresponse.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 6054281..1629a7c 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -5,6 +5,13 @@ import ( "time" ) +type Transaction struct { + CreatedAt time.Time `json:"createdAt"` + Status string `json:"status"` + TransferValue json.Number `json:"transferValue"` + TxHash string `json:"txHash"` + TxType string `json:"txType"` +} type TrackStatusResponse struct { Ok bool `json:"ok"` @@ -17,4 +24,4 @@ type TrackStatusResponse struct { TxType string `json:"txType"` } } `json:"result"` -} \ No newline at end of file +} From 9f8fcf1ed07044fe2c53224d96753689bb736d42 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 12:54:11 +0300 Subject: [PATCH 072/175] attach account service to handler --- cmd/africastalking/main.go | 4 +++- cmd/async/main.go | 4 +++- cmd/http/main.go | 5 +++-- cmd/main.go | 4 +++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 051c27b..91683d2 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -17,6 +17,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/handlers/server" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -127,7 +128,8 @@ func main() { os.Exit(1) } - hl, err := lhs.GetHandler() + accountService := server.AccountService{} + hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) diff --git a/cmd/async/main.go b/cmd/async/main.go index a945d3c..467a0c3 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -14,6 +14,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -94,8 +95,9 @@ func main() { lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) + accountService := server.AccountService{} - hl, err := lhs.GetHandler() + hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) diff --git a/cmd/http/main.go b/cmd/http/main.go index 1d99c51..597695d 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -16,6 +16,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/handlers/server" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -87,8 +88,8 @@ func main() { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } - - hl, err := lhs.GetHandler() + accountService := server.AccountService{} + hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) diff --git a/cmd/main.go b/cmd/main.go index e87fc5a..684e69e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -11,6 +11,7 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -85,7 +86,8 @@ func main() { os.Exit(1) } - hl, err := lhs.GetHandler() + accountService := server.AccountService{} + hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) From 6727bd3769168bacecbb940930e9373710e2065d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 17 Oct 2024 12:55:47 +0300 Subject: [PATCH 073/175] polished code --- cmd/africastalking/main.go | 1 - internal/storage/storageservice.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index b810639..c52094a 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -29,7 +29,6 @@ var ( ) func init() { - fmt.Println("Running init") initializers.LoadEnvVariables() } diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index d99e0ca..ec2b710 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -68,7 +68,7 @@ func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persis func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { database, ok := ctx.Value("Database").(string) if !ok { - fmt.Println("The database is not set") + return nil, fmt.Errorf("failed to select the database") } if database == "postgres" { From eea3be3a39c1fe5ee1be7ea2f7c7ad7a5a8e3125 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 17 Oct 2024 13:49:56 +0300 Subject: [PATCH 074/175] resolve failing test --- internal/handlers/ussd/menuhandler_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index c624e0f..499c426 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1440,7 +1440,6 @@ func TestValidateAmount(t *testing.T) { t.Logf(err.Error()) } flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") - flag_api_error, _ := fm.GetFlag("flag_api_call_error") mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) @@ -1466,7 +1465,6 @@ func TestValidateAmount(t *testing.T) { activeBal: []byte("0.003"), expectedResult: resource.Result{ Content: "0.001", - FlagReset: []uint32{flag_api_error}, }, }, { @@ -1475,7 +1473,6 @@ func TestValidateAmount(t *testing.T) { activeBal: []byte("0.003"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, - FlagReset: []uint32{flag_api_error}, Content: "0.02", }, }, @@ -1485,7 +1482,6 @@ func TestValidateAmount(t *testing.T) { balance: "0.003 CELO", expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, - FlagReset: []uint32{flag_api_error}, Content: "0.02ms", }, }, From fd8bfae8c73f454e5cbb140d61dd9e2ef03557a2 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 17 Oct 2024 15:35:35 +0300 Subject: [PATCH 075/175] updated the vise version to include the updated column --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index dcd1d13..3f68e5d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.22.6 require ( - git.defalsify.org/vise.git v0.2.0 + git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b github.com/alecthomas/assert/v2 v2.2.2 github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 diff --git a/go.sum b/go.sum index 0f7275c..9615d3b 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihc git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= git.defalsify.org/vise.git v0.2.0 h1:X2ZgiGRq4C+9qOlDMP0b/oE5QHjVQNT4aEFZB88ST0Q= git.defalsify.org/vise.git v0.2.0/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= +git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= +git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= From 4f043628358f5e3edb7c73ae239b17f07eee8869 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 17 Oct 2024 16:08:18 +0300 Subject: [PATCH 076/175] pass error description to error response --- internal/handlers/server/accountservice.go | 116 ++++++++++++++++++--- 1 file changed, 103 insertions(+), 13 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 1ddf7e7..7bb3dee 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -2,6 +2,7 @@ package server import ( "encoding/json" + "fmt" "io" "net/http" @@ -9,17 +10,21 @@ import ( "git.grassecon.net/urdt/ussd/internal/models" ) +var apiResponse struct { + Ok bool `json:"ok"` + Description string `json:"description"` +} + type AccountServiceInterface interface { CheckBalance(publicKey string) (*models.BalanceResponse, error) - CreateAccount() (*models.AccountResponse, error) + CreateAccount() (*OKResponse, *ErrResponse) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) + TrackAccountStatus(publicKey string) (*OKResponse, *ErrResponse) } type AccountService struct { } -// CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. -// // Parameters: // - trackingId: A unique identifier for the account.This should be obtained from a previous call to // CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the @@ -28,9 +33,9 @@ type AccountService struct { // Returns: // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. -// If no error occurs, this will be nil. +// If no error occurs, this will be nil func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { - resp, err := http.Get(config.TrackStatusURL + trackingId) + resp, err := http.Get(config.BalanceURL + trackingId) if err != nil { return nil, err } @@ -40,12 +45,67 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackSt if err != nil { return nil, err } + var trackResp models.TrackStatusResponse err = json.Unmarshal(body, &trackResp) if err != nil { return nil, err } return &trackResp, nil + +} + +func (as *AccountService) TrackAccountStatus(publicKey string) (*OKResponse, *ErrResponse) { + var errResponse ErrResponse + var okResponse OKResponse + var err error + // Construct the URL with the path parameter + url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + // Set headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-GE-KEY", "xd") + + // Send the request + resp, err := http.DefaultClient.Do(req) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + defer resp.Body.Close() + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + + // Step 2: Unmarshal into the generic struct + err = json.Unmarshal([]byte(body), &apiResponse) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + if apiResponse.Ok { + err = json.Unmarshal([]byte(body), &okResponse) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + return &okResponse, nil + } else { + err := json.Unmarshal([]byte(body), &errResponse) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + return nil, &errResponse + } } // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. @@ -75,20 +135,50 @@ func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceRespons // If there is an error during the request or processing, this will be nil. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { - resp, err := http.Post(config.CreateAccountURL, "application/json", nil) +func (as *AccountService) CreateAccount() (*OKResponse, *ErrResponse) { + + var errResponse ErrResponse + var okResponse OKResponse + var err error + + // Create a new request + req, err := http.NewRequest("POST", config.CreateAccountURL, nil) if err != nil { - return nil, err + errResponse.Description = err.Error() + return nil, &errResponse + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-GE-KEY", "xd") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse } defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) if err != nil { - return nil, err + errResponse.Description = err.Error() + return nil, &errResponse } - var accountResp models.AccountResponse - err = json.Unmarshal(body, &accountResp) + err = json.Unmarshal([]byte(body), &apiResponse) if err != nil { - return nil, err + return nil, &errResponse + } + if apiResponse.Ok { + err = json.Unmarshal([]byte(body), &okResponse) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + return &okResponse, nil + } else { + err := json.Unmarshal([]byte(body), &errResponse) + if err != nil { + errResponse.Description = err.Error() + return nil, &errResponse + } + return nil, &errResponse } - return &accountResp, nil } From b8e12e5215ec48c49dc7673a0dd9509a155df5e1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:01:21 +0300 Subject: [PATCH 077/175] update api endpoints --- config/config.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/config/config.go b/config/config.go index 0571503..a035d24 100644 --- a/config/config.go +++ b/config/config.go @@ -1,10 +1,7 @@ package config - - const ( - CreateAccountURL = "https://custodial.sarafu.africa/api/account/create" - TrackStatusURL = "https://custodial.sarafu.africa/api/track/" - BalanceURL = "https://custodial.sarafu.africa/api/account/status/" + CreateAccountURL = "http://localhost:5003/api/v2/account/create" + BalanceURL = "https://custodial.sarafu.africa/api/account/status/" + TrackURL = "http://localhost:5003/api/v2/account/status" ) - From 81c4189c8eaf254bec11a7a78681dc83e9d8b1ad Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:02:08 +0300 Subject: [PATCH 078/175] update tests --- internal/handlers/ussd/menuhandler_test.go | 142 +++++++++++---------- 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 0bde51a..97c0dee 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -15,6 +15,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" + "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/mocks" "git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/utils" @@ -78,68 +79,75 @@ func TestCreateAccount(t *testing.T) { if err != nil { t.Logf(err.Error()) } - // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - expectedResult := resource.Result{} - accountCreatedFlag, err := fm.GetFlag("flag_account_created") - + flag_account_created, err := fm.GetFlag("flag_account_created") if err != nil { t.Logf(err.Error()) } - expectedResult.FlagSet = append(expectedResult.FlagSet, accountCreatedFlag) // Define session ID and mock data sessionId := "session123" - typ := utils.DATA_ACCOUNT_CREATED - fakeError := db.ErrNotFound{} - // Create context with session ID + notFoundErr := db.ErrNotFound{} ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte("123"), fakeError) - expectedAccountResp := &models.AccountResponse{ - Ok: true, - Result: struct { - CustodialId json.Number `json:"custodialId"` - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` - }{ - CustodialId: "12", - PublicKey: "0x8E0XSCSVA", - TrackingId: "d95a7e83-196c-4fd0-866fSGAGA", + tests := []struct { + name string + serverResponse *server.OKResponse + expectedResult resource.Result + }{ + { + name: "Test account creation success", + serverResponse: &server.OKResponse{ + Ok: true, + Description: "Account creation successed", + Result: map[string]any{ + "trackingId": "1234567890", + "publicKey": "1235QERYU", + }, + }, + expectedResult: resource.Result{ + FlagSet: []uint32{flag_account_created}, + }, }, } - mockCreateAccountService.On("CreateAccount").Return(expectedAccountResp, nil) - data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: expectedAccountResp.Result.TrackingId, - utils.DATA_PUBLIC_KEY: expectedAccountResp.Result.PublicKey, - utils.DATA_CUSTODIAL_ID: expectedAccountResp.Result.CustodialId.String(), + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + mockDataStore := new(mocks.MockUserDataStore) + mockCreateAccountService := new(mocks.MockAccountService) + + // Create a Handlers instance with the mock data store + h := &Handlers{ + userdataStore: mockDataStore, + accountService: mockCreateAccountService, + flagManager: fm.parser, + } + + data := map[utils.DataTyp]string{ + utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), + utils.DATA_PUBLIC_KEY: tt.serverResponse.Result["publicKey"].(string), + } + + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) + mockCreateAccountService.On("CreateAccount").Return(tt.serverResponse, nil) + + for key, value := range data { + mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil) + } + + // Call the method you want to test + res, err := h.CreateAccount(ctx, "create_account", []byte("some-input")) + + // Assert that no errors occurred + assert.NoError(t, err) + + // Assert that the account created flag has been set to the result + assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") + + // Assert that expectations were met + mockDataStore.AssertExpectations(t) + }) } - - for key, value := range data { - mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil) - } - - // Create a Handlers instance with the mock data store - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, - flagManager: fm.parser, - } - - // Call the method you want to test - res, err := h.CreateAccount(ctx, "create_account", []byte("some-input")) - - // Assert that no errors occurred - assert.NoError(t, err) - - //Assert that the account created flag has been set to the result - assert.Equal(t, res, expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) } func TestWithPersister(t *testing.T) { @@ -1072,12 +1080,20 @@ func TestCheckAccountStatus(t *testing.T) { tests := []struct { name string input []byte + serverResponse *server.OKResponse response *models.TrackStatusResponse expectedResult resource.Result }{ { - name: "Test when account status is Success", + name: "Test when account is on the Sarafu network", input: []byte("TrackingId1234"), + serverResponse: &server.OKResponse{ + Ok: true, + Description: "Account creation successed", + Result: map[string]any{ + "active": true, + }, + }, response: &models.TrackStatusResponse{ Ok: true, Result: struct { @@ -1104,17 +1120,7 @@ func TestCheckAccountStatus(t *testing.T) { }, }, { - name: "Test when fetching account status is not Success", - input: []byte("TrackingId1234"), - response: &models.TrackStatusResponse{ - Ok: false, - }, - expectedResult: resource.Result{ - FlagSet: []uint32{flag_api_error}, - }, - }, - { - name: "Test when checking account status api call is a SUCCESS but an account is not yet ready", + name: "Test when the account is not yet on the sarafu network", input: []byte("TrackingId1234"), response: &models.TrackStatusResponse{ Ok: true, @@ -1129,13 +1135,20 @@ func TestCheckAccountStatus(t *testing.T) { }{ Transaction: Transaction{ CreatedAt: time.Now(), - Status: "IN_NETWORK", + Status: "SUCCESS", TransferValue: json.Number("0.5"), TxHash: "0x123abc456def", TxType: "transfer", }, }, }, + serverResponse: &server.OKResponse{ + Ok: true, + Description: "Account creation successed", + Result: map[string]any{ + "active": false, + }, + }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_pending}, FlagReset: []uint32{flag_api_error, flag_account_success}, @@ -1155,9 +1168,10 @@ func TestCheckAccountStatus(t *testing.T) { status := tt.response.Result.Transaction.Status // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TRACKING_ID).Return(tt.input, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.input, nil) mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) + mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() // Call the method under test From 128a354b34f2ed7f2eb44d99054dc4ac9d161a2c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:03:48 +0300 Subject: [PATCH 079/175] use api structs --- internal/handlers/ussd/menuhandler.go | 48 +++++++++++---------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 6059dd4..adf4111 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -27,6 +27,8 @@ var ( logg = logging.NewVanilla().WithDomain("ussdmenuhandler") scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") + okResponse *server.OKResponse + errResponse *server.ErrResponse ) // FlagManager handles centralized flag management @@ -136,11 +138,16 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r } func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { - accountResp, err := h.accountService.CreateAccount() + okResponse, errResponse := h.accountService.CreateAccount() + if errResponse != nil { + return nil + } + trackingId := okResponse.Result["trackingId"].(string) + publicKey := okResponse.Result["publicKey"].(string) + data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: accountResp.Result.TrackingId, - utils.DATA_PUBLIC_KEY: accountResp.Result.PublicKey, - utils.DATA_CUSTODIAL_ID: accountResp.Result.CustodialId.String(), + utils.DATA_TRACKING_ID: trackingId, + utils.DATA_PUBLIC_KEY: publicKey, } for key, value := range data { @@ -152,7 +159,7 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r } flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") res.FlagSet = append(res.FlagSet, flag_account_created) - return err + return nil } @@ -191,7 +198,6 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou } flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - accountPIN := string(input) // Validate that the PIN is a 4-digit number if !isValidPIN(accountPIN) { @@ -368,7 +374,6 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou if !ok { return res, fmt.Errorf("missing session") } - if len(input) == 4 { yob := string(input) store := h.userdataStore @@ -411,7 +416,6 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re if !ok { return res, fmt.Errorf("missing session") } - gender := strings.Split(symbol, "_")[1] store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) @@ -430,7 +434,6 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { offerings := string(input) store := h.userdataStore @@ -456,7 +459,6 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt // ResetAccountAuthorized resets the account authorization flag after a successful PIN entry. func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") res.FlagReset = append(res.FlagReset, flag_account_authorized) @@ -466,12 +468,10 @@ func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input // CheckIdentifier retrieves the PublicKey from the JSON data file. func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - store := h.userdataStore publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) @@ -485,12 +485,10 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error - sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") @@ -542,28 +540,20 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b return res, fmt.Errorf("missing session") } store := h.userdataStore - trackingId, err := store.ReadEntry(ctx, sessionId, utils.DATA_TRACKING_ID) + publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { return res, err } - - accountStatus, err := h.accountService.CheckAccountStatus(string(trackingId)) - if err != nil { - fmt.Println("Error checking account status:", err) - return res, err - } - if !accountStatus.Ok { - res.FlagSet = append(res.FlagSet, flag_api_error) + okResponse, errResponse = h.accountService.TrackAccountStatus(string(publicKey)) + if errResponse != nil { return res, err } res.FlagReset = append(res.FlagReset, flag_api_error) - status := accountStatus.Result.Transaction.Status - - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)) - if err != nil { - return res, nil + isActive := okResponse.Result["active"].(bool) + if !ok { + return res, err } - if accountStatus.Result.Transaction.Status == "SUCCESS" { + if isActive { res.FlagSet = append(res.FlagSet, flag_account_success) res.FlagReset = append(res.FlagReset, flag_account_pending) } else { From 1c57c95d93efdf6d26a795f1987a9da8a712bac2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:04:02 +0300 Subject: [PATCH 080/175] use api structs --- internal/mocks/servicemock.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index d828045..7796b77 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -1,6 +1,7 @@ package mocks import ( + "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/models" "github.com/stretchr/testify/mock" ) @@ -10,9 +11,19 @@ type MockAccountService struct { mock.Mock } -func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { +func (m *MockAccountService) CreateAccount() (*server.OKResponse, *server.ErrResponse) { args := m.Called() - return args.Get(0).(*models.AccountResponse), args.Error(1) + okResponse, ok := args.Get(0).(*server.OKResponse) + errResponse, err := args.Get(1).(*server.ErrResponse) + + if ok { + return okResponse, nil + } + + if err { + return nil, errResponse + } + return nil, nil } func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { @@ -23,4 +34,17 @@ func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResp func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { args := m.Called(trackingId) return args.Get(0).(*models.TrackStatusResponse), args.Error(1) -} \ No newline at end of file +} + +func (m *MockAccountService) TrackAccountStatus(publicKey string) (*server.OKResponse, *server.ErrResponse) { + args := m.Called(publicKey) + okResponse, ok := args.Get(0).(*server.OKResponse) + errResponse, err := args.Get(1).(*server.ErrResponse) + if ok { + return okResponse, nil + } + if err { + return nil, errResponse + } + return nil, nil +} From 353e24de33e79f79705b9022edc6a59ce3ce4245 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:04:43 +0300 Subject: [PATCH 081/175] define api structs --- internal/models/accountresponse.go | 17 ++++++----------- internal/models/trackstatusresponse.go | 3 +-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/internal/models/accountresponse.go b/internal/models/accountresponse.go index 1422a20..efcc30b 100644 --- a/internal/models/accountresponse.go +++ b/internal/models/accountresponse.go @@ -1,15 +1,10 @@ package models -import ( - "encoding/json" - -) - type AccountResponse struct { - Ok bool `json:"ok"` - Result struct { - CustodialId json.Number `json:"custodialId"` - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` + Ok bool `json:"ok"` + Description string `json:"description"` // Include the description field + Result struct { + PublicKey string `json:"publicKey"` + TrackingId string `json:"trackingId"` } `json:"result"` -} \ No newline at end of file +} diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 6054281..667c544 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -5,7 +5,6 @@ import ( "time" ) - type TrackStatusResponse struct { Ok bool `json:"ok"` Result struct { @@ -17,4 +16,4 @@ type TrackStatusResponse struct { TxType string `json:"txType"` } } `json:"result"` -} \ No newline at end of file +} From b8860478b6f469572ec13f68b4d07215cbf4a9f9 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:36:51 +0300 Subject: [PATCH 082/175] implement expected structs --- internal/handlers/server/accountservice.go | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 451c661..c284d63 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -187,19 +187,13 @@ func (as *AccountService) CreateAccount() (*OKResponse, *ErrResponse) { } } -func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { - return &models.AccountResponse{ - Ok: true, - Result: struct { - CustodialId json.Number `json:"custodialId"` - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` - }{ - CustodialId: json.Number("182"), - PublicKey: "0x48ADca309b5085852207FAaf2816eD72B52F527C", - TrackingId: "28ebe84d-b925-472c-87ae-bbdfa1fb97be", - }, +func (tas *TestAccountService) CreateAccount() (*OKResponse, *ErrResponse) { + return &OKResponse{ + Ok: true, + Description: "Account creation request received successfully", + Result: map[string]any{"publicKey": "0x48ADca309b5085852207FAaf2816eD72B52F527C", "trackingId": "28ebe84d-b925-472c-87ae-bbdfa1fb97be"}, }, nil + } func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { @@ -218,6 +212,16 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe return balanceResponse, nil } +func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*OKResponse, *ErrResponse) { + return &OKResponse{ + Ok: true, + Description: "Account creation succeeded", + Result: map[string]any{ + "active": true, + }, + }, nil +} + func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { trackResponse := &models.TrackStatusResponse{ Ok: true, From f643aa4d14091a13f62bee23b73d736de7170bac Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 18 Oct 2024 17:37:04 +0300 Subject: [PATCH 083/175] update tests --- internal/handlers/ussd/menuhandler_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index de1a481..f84ca06 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1082,7 +1082,7 @@ func TestCheckAccountStatus(t *testing.T) { input: []byte("TrackingId1234"), serverResponse: &server.OKResponse{ Ok: true, - Description: "Account creation successed", + Description: "Account creation succeeded", Result: map[string]any{ "active": true, }, @@ -1137,7 +1137,7 @@ func TestCheckAccountStatus(t *testing.T) { }, serverResponse: &server.OKResponse{ Ok: true, - Description: "Account creation successed", + Description: "Account creation succeeded", Result: map[string]any{ "active": false, }, From 25bc7006a4d743f0a74e9d64b3703b59e025d8ae Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 18 Oct 2024 18:31:40 +0300 Subject: [PATCH 084/175] show a balance of 0 and prevent sending when no voucher exists --- internal/handlers/ussd/menuhandler.go | 18 ++++++++++++------ services/registration/no_voucher | 1 + services/registration/no_voucher.vis | 5 +++++ services/registration/no_voucher_swa | 1 + services/registration/pp.csv | 3 ++- services/registration/send.vis | 1 + 6 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 services/registration/no_voucher create mode 100644 services/registration/no_voucher.vis create mode 100644 services/registration/no_voucher_swa diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4f010d6..93acc02 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -624,7 +624,7 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by return res, nil } -// CheckBalance retrieves the balance from the API using the "PublicKey" and sets +// CheckBalance retrieves the balance of the active voucher and sets // the balance as the result content func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -640,12 +640,13 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( // get the active sym and active balance activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) if err != nil { - return res, err + res.Content = "0.00" + return res, nil } activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) if err != nil { - return res, err + return res, nil } res.Content = fmt.Sprintf("%s %s", activeBal, activeSym) @@ -991,6 +992,8 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by return res, fmt.Errorf("missing session") } + flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") + // check if the user has an active sym _, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) @@ -1008,9 +1011,10 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by return res, nil } - // Ensure there is at least one voucher + // Return if there is no voucher if len(vouchersResp.Result.Holdings) == 0 { - return res, err + res.FlagSet = append(res.FlagSet, flag_no_active_voucher) + return res, nil } // Use only the first voucher @@ -1035,6 +1039,8 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by return res, err } + res.FlagReset = append(res.FlagReset, flag_no_active_voucher) + return res, nil } @@ -1160,7 +1166,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r if err != nil { return res, err } - + res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) } else { diff --git a/services/registration/no_voucher b/services/registration/no_voucher new file mode 100644 index 0000000..332f00e --- /dev/null +++ b/services/registration/no_voucher @@ -0,0 +1 @@ +You need a voucher to send \ No newline at end of file diff --git a/services/registration/no_voucher.vis b/services/registration/no_voucher.vis new file mode 100644 index 0000000..832ef22 --- /dev/null +++ b/services/registration/no_voucher.vis @@ -0,0 +1,5 @@ +MOUT back 0 +MOUT quit 9 +HALT +INCMP ^ 0 +INCMP quit 9 diff --git a/services/registration/no_voucher_swa b/services/registration/no_voucher_swa new file mode 100644 index 0000000..66e8f26 --- /dev/null +++ b/services/registration/no_voucher_swa @@ -0,0 +1 @@ +Unahitaji sarafu kutuma \ No newline at end of file diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 96d3e8a..ec0d8c1 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -15,4 +15,5 @@ flag,flag_allow_update,21,this is set to allow a user to update their profile da flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid -flag,flag_api_call_error,25,this is set when communication to an external service fails +flag,flag_api_call_error,25,this is set when communication to an external service fails +flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher diff --git a/services/registration/send.vis b/services/registration/send.vis index e120302..0ff0927 100644 --- a/services/registration/send.vis +++ b/services/registration/send.vis @@ -1,4 +1,5 @@ LOAD transaction_reset 0 +CATCH no_voucher flag_no_active_voucher 1 MOUT back 0 HALT LOAD validate_recipient 20 From 00a2beae503980693cf6a8d7f458a006671a66a7 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 19 Oct 2024 13:41:19 +0300 Subject: [PATCH 085/175] rename file --- initializers/{loadEnvVariables.go => load.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename initializers/{loadEnvVariables.go => load.go} (100%) diff --git a/initializers/loadEnvVariables.go b/initializers/load.go similarity index 100% rename from initializers/loadEnvVariables.go rename to initializers/load.go From f13f5996c16c64a06713f7c9cf347edf983635f9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 19 Oct 2024 14:51:41 +0300 Subject: [PATCH 086/175] remove thread abstraction from postgres --- internal/storage/postgres.go | 133 ++++++++++------------------- internal/storage/storageservice.go | 2 +- 2 files changed, 47 insertions(+), 88 deletions(-) diff --git a/internal/storage/postgres.go b/internal/storage/postgres.go index ed4a711..9422a26 100644 --- a/internal/storage/postgres.go +++ b/internal/storage/postgres.go @@ -8,27 +8,18 @@ import ( "git.defalsify.org/vise.git/lang" ) -var ( - pdbC map[string]chan db.Db -) - -type ThreadPostgresDb struct { +type PostgresDb struct { db db.Db connStr string } -func NewThreadPostgresDb() *ThreadPostgresDb { - if pdbC == nil { - pdbC = make(map[string]chan db.Db) - } - return &ThreadPostgresDb{} +func NewPostgresDb() *PostgresDb { + return &PostgresDb{} } -func (tpdb *ThreadPostgresDb) Connect(ctx context.Context, connStr string) error { - var ok bool - _, ok = pdbC[connStr] - if ok { - logg.WarnCtxf(ctx, "already registered thread postgres, skipping", "connStr", connStr) +func (pdb *PostgresDb) Connect(ctx context.Context, connStr string) error { + if pdb.db != nil { + logg.WarnCtxf(ctx, "already connected, skipping", "connStr", connStr) return nil } postgresdb := postgres.NewPgDb().WithSchema("public") @@ -36,80 +27,48 @@ func (tpdb *ThreadPostgresDb) Connect(ctx context.Context, connStr string) error if err != nil { return err } - pdbC[connStr] = make(chan db.Db, 1) - pdbC[connStr] <- postgresdb - tpdb.connStr = connStr + pdb.db = postgresdb + pdb.connStr = connStr return nil } -func (tpdb *ThreadPostgresDb) reserve() { - if tpdb.db == nil { - tpdb.db = <-pdbC[tpdb.connStr] +func (pdb *PostgresDb) SetPrefix(pfx uint8) { + pdb.db.SetPrefix(pfx) +} + +func (pdb *PostgresDb) SetSession(sessionId string) { + pdb.db.SetSession(sessionId) +} + +func (pdb *PostgresDb) SetLanguage(lng *lang.Language) { + pdb.db.SetLanguage(lng) +} + +func (pdb *PostgresDb) Safe() bool { + return pdb.db.Safe() +} + +func (pdb *PostgresDb) Prefix() uint8 { + return pdb.db.Prefix() +} + +func (pdb *PostgresDb) SetLock(typ uint8, locked bool) error { + return pdb.db.SetLock(typ, locked) +} + +func (pdb *PostgresDb) Put(ctx context.Context, key []byte, val []byte) error { + return pdb.db.Put(ctx, key, val) +} + +func (pdb *PostgresDb) Get(ctx context.Context, key []byte) ([]byte, error) { + return pdb.db.Get(ctx, key) +} + +func (pdb *PostgresDb) Close() error { + if pdb.db == nil { + return nil } -} - -func (tpdb *ThreadPostgresDb) release() { - if tpdb.db == nil { - return - } - pdbC[tpdb.connStr] <- tpdb.db - tpdb.db = nil -} - -func (tpdb *ThreadPostgresDb) SetPrefix(pfx uint8) { - tpdb.reserve() - tpdb.db.SetPrefix(pfx) -} - -func (tpdb *ThreadPostgresDb) SetSession(sessionId string) { - tpdb.reserve() - tpdb.db.SetSession(sessionId) -} - -func (tpdb *ThreadPostgresDb) SetLanguage(lng *lang.Language) { - tpdb.reserve() - tpdb.db.SetLanguage(lng) -} - -func (tpdb *ThreadPostgresDb) Safe() bool { - tpdb.reserve() - v := tpdb.db.Safe() - tpdb.release() - return v -} - -func (tpdb *ThreadPostgresDb) Prefix() uint8 { - tpdb.reserve() - v := tpdb.db.Prefix() - tpdb.release() - return v -} - -func (tpdb *ThreadPostgresDb) SetLock(typ uint8, locked bool) error { - tpdb.reserve() - err := tpdb.db.SetLock(typ, locked) - tpdb.release() + err := pdb.db.Close() + pdb.db = nil return err -} - -func (tpdb *ThreadPostgresDb) Put(ctx context.Context, key []byte, val []byte) error { - tpdb.reserve() - err := tpdb.db.Put(ctx, key, val) - tpdb.release() - return err -} - -func (tpdb *ThreadPostgresDb) Get(ctx context.Context, key []byte) ([]byte, error) { - tpdb.reserve() - v, err := tpdb.db.Get(ctx, key) - tpdb.release() - return v, err -} - -func (tpdb *ThreadPostgresDb) Close() error { - tpdb.reserve() - close(pdbC[tpdb.connStr]) - err := tpdb.db.Close() - tpdb.db = nil - return err -} +} \ No newline at end of file diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index ec2b710..01a122c 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -72,7 +72,7 @@ func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) } if database == "postgres" { - ms.userDataStore = NewThreadPostgresDb() + ms.userDataStore = NewPostgresDb() connStr := buildConnStr() err := ms.userDataStore.Connect(ctx, connStr) if err != nil { From a40fc37da4ba015e3505e2f2bd387c16706707a6 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 19 Oct 2024 15:27:23 +0300 Subject: [PATCH 087/175] implement postgres for the state store --- internal/storage/storageservice.go | 81 ++++++++++++++++++------------ 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index 01a122c..c616019 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -53,40 +53,58 @@ func NewMenuStorageService(dbDir string, resourceDir string) *MenuStorageService } } -func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { - ms.stateStore = NewThreadGdbmDb() - storeFile := path.Join(ms.dbDir, "state.gdbm") - err := ms.stateStore.Connect(ctx, storeFile) - if err != nil { - return nil, err - } - pr := persist.NewPersister(ms.stateStore) - logg.TraceCtxf(ctx, "menu storage service", "persist", pr, "store", ms.stateStore) - return pr, nil -} - -func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { +func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.Db, fileName string) (db.Db, error) { database, ok := ctx.Value("Database").(string) if !ok { return nil, fmt.Errorf("failed to select the database") } - if database == "postgres" { - ms.userDataStore = NewPostgresDb() - connStr := buildConnStr() - err := ms.userDataStore.Connect(ctx, connStr) - if err != nil { - return nil, err - } - } else { - ms.userDataStore = NewThreadGdbmDb() - storeFile := path.Join(ms.dbDir, "userdata.gdbm") - err := ms.userDataStore.Connect(ctx, storeFile) - if err != nil { - return nil, err - } + if existingDb != nil { + return existingDb, nil } + var newDb db.Db + var err error + + if database == "postgres" { + newDb = NewPostgresDb() + connStr := buildConnStr() + err = newDb.Connect(ctx, connStr) + } else { + newDb = NewThreadGdbmDb() + storeFile := path.Join(ms.dbDir, fileName) + err = newDb.Connect(ctx, storeFile) + } + + if err != nil { + return nil, err + } + + return newDb, nil +} + +func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { + stateStore, err := ms.GetStateStore(ctx) + if err != nil { + return nil, err + } + + pr := persist.NewPersister(stateStore) + logg.TraceCtxf(ctx, "menu storage service", "persist", pr, "store", stateStore) + return pr, nil +} + +func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { + if ms.userDataStore != nil { + return ms.userDataStore, nil + } + + userDataStore, err := ms.getOrCreateDb(ctx, ms.userDataStore, "userdata.gdbm") + if err != nil { + return nil, err + } + + ms.userDataStore = userDataStore return ms.userDataStore, nil } @@ -102,14 +120,15 @@ func (ms *MenuStorageService) GetResource(ctx context.Context) (resource.Resourc func (ms *MenuStorageService) GetStateStore(ctx context.Context) (db.Db, error) { if ms.stateStore != nil { - panic("set up store when already exists") + return ms.stateStore, nil } - ms.stateStore = NewThreadGdbmDb() - storeFile := path.Join(ms.dbDir, "state.gdbm") - err := ms.stateStore.Connect(ctx, storeFile) + + stateStore, err := ms.getOrCreateDb(ctx, ms.stateStore, "state.gdbm") if err != nil { return nil, err } + + ms.stateStore = stateStore return ms.stateStore, nil } From 3bf2045f1552e365113b90861779ac358a55a361 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 19 Oct 2024 16:07:40 +0300 Subject: [PATCH 088/175] added FetchVouchers to the TestAccountService --- internal/handlers/server/accountservice.go | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index e38e13d..ebe825e 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -132,7 +132,6 @@ func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) } func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { - balanceResponse := &models.BalanceResponse{ Ok: true, Result: struct { @@ -170,3 +169,31 @@ func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.Tr } return trackResponse, nil } + +func (tas *TestAccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { + return &models.VoucherHoldingResponse{ + Ok: true, + Result: struct { + Holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + } `json:"holdings"` + }{ + Holdings: []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{ + { + ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", + TokenSymbol: "SRF", + TokenDecimals: "6", + Balance: "2745987", + }, + }, + }, + }, nil +} From 9f562fe53e58ca9511335e4c8aae15ec58b9ac57 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 19 Oct 2024 23:06:58 +0300 Subject: [PATCH 089/175] use postgres directly from go-vise --- internal/storage/postgres.go | 74 ------------------------------ internal/storage/storageservice.go | 3 +- 2 files changed, 2 insertions(+), 75 deletions(-) delete mode 100644 internal/storage/postgres.go diff --git a/internal/storage/postgres.go b/internal/storage/postgres.go deleted file mode 100644 index 9422a26..0000000 --- a/internal/storage/postgres.go +++ /dev/null @@ -1,74 +0,0 @@ -package storage - -import ( - "context" - - "git.defalsify.org/vise.git/db" - postgres "git.defalsify.org/vise.git/db/postgres" - "git.defalsify.org/vise.git/lang" -) - -type PostgresDb struct { - db db.Db - connStr string -} - -func NewPostgresDb() *PostgresDb { - return &PostgresDb{} -} - -func (pdb *PostgresDb) Connect(ctx context.Context, connStr string) error { - if pdb.db != nil { - logg.WarnCtxf(ctx, "already connected, skipping", "connStr", connStr) - return nil - } - postgresdb := postgres.NewPgDb().WithSchema("public") - err := postgresdb.Connect(ctx, connStr) - if err != nil { - return err - } - pdb.db = postgresdb - pdb.connStr = connStr - return nil -} - -func (pdb *PostgresDb) SetPrefix(pfx uint8) { - pdb.db.SetPrefix(pfx) -} - -func (pdb *PostgresDb) SetSession(sessionId string) { - pdb.db.SetSession(sessionId) -} - -func (pdb *PostgresDb) SetLanguage(lng *lang.Language) { - pdb.db.SetLanguage(lng) -} - -func (pdb *PostgresDb) Safe() bool { - return pdb.db.Safe() -} - -func (pdb *PostgresDb) Prefix() uint8 { - return pdb.db.Prefix() -} - -func (pdb *PostgresDb) SetLock(typ uint8, locked bool) error { - return pdb.db.SetLock(typ, locked) -} - -func (pdb *PostgresDb) Put(ctx context.Context, key []byte, val []byte) error { - return pdb.db.Put(ctx, key, val) -} - -func (pdb *PostgresDb) Get(ctx context.Context, key []byte) ([]byte, error) { - return pdb.db.Get(ctx, key) -} - -func (pdb *PostgresDb) Close() error { - if pdb.db == nil { - return nil - } - err := pdb.db.Close() - pdb.db = nil - return err -} \ No newline at end of file diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index c616019..9fa1839 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -8,6 +8,7 @@ import ( "git.defalsify.org/vise.git/db" fsdb "git.defalsify.org/vise.git/db/fs" + "git.defalsify.org/vise.git/db/postgres" "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" @@ -67,7 +68,7 @@ func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.D var err error if database == "postgres" { - newDb = NewPostgresDb() + newDb = postgres.NewPgDb() connStr := buildConnStr() err = newDb.Connect(ctx, connStr) } else { From 415c8074646cc3bacba0e1b112ef4c52a702e771 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 21 Oct 2024 09:22:07 +0300 Subject: [PATCH 090/175] include the Database in context --- internal/testutil/TestEngine.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index 2432a3f..75ac817 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -25,6 +25,7 @@ var ( func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "Database", "gdbm") pfp := path.Join(scriptDir, "pp.csv") var eventChannel = make(chan bool) From 367d3f0593c4ed5621a32e4187bfb0558be200ac Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 11:07:32 +0300 Subject: [PATCH 091/175] remove manually added api.go file --- internal/handlers/server/api.go | 60 --------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 internal/handlers/server/api.go diff --git a/internal/handlers/server/api.go b/internal/handlers/server/api.go deleted file mode 100644 index 646458c..0000000 --- a/internal/handlers/server/api.go +++ /dev/null @@ -1,60 +0,0 @@ -package server - -type ( - OKResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` - Result map[string]any `json:"result"` - } - - ErrResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` - ErrCode string `json:"errorCode"` - } - - TransferRequest struct { - From string `json:"from" validate:"required,eth_addr_checksum"` - To string `json:"to" validate:"required,eth_addr_checksum"` - TokenAddress string `json:"tokenAddress" validate:"required,eth_addr_checksum"` - Amount string `json:"amount" validate:"required,number,gt=0"` - } - - PoolSwapRequest struct { - From string `json:"from" validate:"required,eth_addr_checksum"` - FromTokenAddress string `json:"fromTokenAddress" validate:"required,eth_addr_checksum"` - ToTokenAddress string `json:"toTokenAddress" validate:"required,eth_addr_checksum"` - PoolAddress string `json:"poolAddress" validate:"required,eth_addr_checksum"` - Amount string `json:"amount" validate:"required,number,gt=0"` - } - - PoolDepositRequest struct { - From string `json:"from" validate:"required,eth_addr_checksum"` - TokenAddress string `json:"tokenAddress" validate:"required,eth_addr_checksum"` - PoolAddress string `json:"poolAddress" validate:"required,eth_addr_checksum"` - Amount string `json:"amount" validate:"required,number,gt=0"` - } - - AccountAddressParam struct { - Address string `param:"address" validate:"required,eth_addr_checksum"` - } - - TrackingIDParam struct { - TrackingID string `param:"trackingId" validate:"required,uuid"` - } - - OTXByAccountRequest struct { - Address string `param:"address" validate:"required,eth_addr_checksum"` - PerPage int `query:"perPage" validate:"required,number,gt=0"` - Cursor int `query:"cursor" validate:"number"` - Next bool `query:"next"` - } -) - -const ( - ErrCodeInternalServerError = "E01" - ErrCodeInvalidJSON = "E02" - ErrCodeInvalidAPIKey = "E03" - ErrCodeValidationFailed = "E04" - ErrCodeAccountNotExists = "E05" -) From 5b0a38351325bcf43eae700f5a06ac4e8bd8883b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 11:09:37 +0300 Subject: [PATCH 092/175] use importable api structs --- internal/handlers/server/accountservice.go | 29 ++++++++++++---------- internal/handlers/ussd/menuhandler_test.go | 14 +++++++---- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index c284d63..39d1c66 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -9,18 +9,19 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" + "github.com/grassrootseconomics/eth-custodial/pkg/api" ) -var apiResponse struct { +type ApiResponse struct { Ok bool `json:"ok"` Description string `json:"description"` } type AccountServiceInterface interface { CheckBalance(publicKey string) (*models.BalanceResponse, error) - CreateAccount() (*OKResponse, *ErrResponse) + CreateAccount() (*api.OKResponse, *api.ErrResponse) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) - TrackAccountStatus(publicKey string) (*OKResponse, *ErrResponse) + TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) } type AccountService struct { @@ -59,9 +60,9 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackSt } -func (as *AccountService) TrackAccountStatus(publicKey string) (*OKResponse, *ErrResponse) { - var errResponse ErrResponse - var okResponse OKResponse +func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { + var errResponse api.ErrResponse + var okResponse api.OKResponse var err error // Construct the URL with the path parameter url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) @@ -90,6 +91,7 @@ func (as *AccountService) TrackAccountStatus(publicKey string) (*OKResponse, *Er } // Step 2: Unmarshal into the generic struct + var apiResponse ApiResponse err = json.Unmarshal([]byte(body), &apiResponse) if err != nil { errResponse.Description = err.Error() @@ -139,10 +141,10 @@ func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceRespons // If there is an error during the request or processing, this will be nil. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -func (as *AccountService) CreateAccount() (*OKResponse, *ErrResponse) { +func (as *AccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { - var errResponse ErrResponse - var okResponse OKResponse + var errResponse api.ErrResponse + var okResponse api.OKResponse var err error // Create a new request @@ -166,6 +168,7 @@ func (as *AccountService) CreateAccount() (*OKResponse, *ErrResponse) { errResponse.Description = err.Error() return nil, &errResponse } + var apiResponse ApiResponse err = json.Unmarshal([]byte(body), &apiResponse) if err != nil { return nil, &errResponse @@ -187,8 +190,8 @@ func (as *AccountService) CreateAccount() (*OKResponse, *ErrResponse) { } } -func (tas *TestAccountService) CreateAccount() (*OKResponse, *ErrResponse) { - return &OKResponse{ +func (tas *TestAccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { + return &api.OKResponse{ Ok: true, Description: "Account creation request received successfully", Result: map[string]any{"publicKey": "0x48ADca309b5085852207FAaf2816eD72B52F527C", "trackingId": "28ebe84d-b925-472c-87ae-bbdfa1fb97be"}, @@ -212,8 +215,8 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe return balanceResponse, nil } -func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*OKResponse, *ErrResponse) { - return &OKResponse{ +func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { + return &api.OKResponse{ Ok: true, Description: "Account creation succeeded", Result: map[string]any{ diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index f84ca06..d78f526 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -20,6 +20,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" + "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" ) @@ -74,6 +75,8 @@ func TestCreateAccount(t *testing.T) { } // Create required mocks flag_account_created, err := fm.GetFlag("flag_account_created") + //flag_api_call_error, err := fm.GetFlag("flag_api_call_error,") + flag_api_call_error, _ := fm.GetFlag("flag_api_call_error") if err != nil { t.Logf(err.Error()) } @@ -85,12 +88,12 @@ func TestCreateAccount(t *testing.T) { tests := []struct { name string - serverResponse *server.OKResponse + serverResponse *api.OKResponse expectedResult resource.Result }{ { name: "Test account creation success", - serverResponse: &server.OKResponse{ + serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation successed", Result: map[string]any{ @@ -100,6 +103,7 @@ func TestCreateAccount(t *testing.T) { }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_created}, + FlagReset: []uint32{flag_api_call_error}, }, }, } @@ -1073,14 +1077,14 @@ func TestCheckAccountStatus(t *testing.T) { tests := []struct { name string input []byte - serverResponse *server.OKResponse + serverResponse *api.OKResponse response *models.TrackStatusResponse expectedResult resource.Result }{ { name: "Test when account is on the Sarafu network", input: []byte("TrackingId1234"), - serverResponse: &server.OKResponse{ + serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation succeeded", Result: map[string]any{ @@ -1135,7 +1139,7 @@ func TestCheckAccountStatus(t *testing.T) { }, }, }, - serverResponse: &server.OKResponse{ + serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation succeeded", Result: map[string]any{ From d81bc0eefb78ba78eac2c61dcdfb1e1413822766 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 11:10:01 +0300 Subject: [PATCH 093/175] use importable api structs --- internal/handlers/ussd/menuhandler.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 3efcbb1..0fc30cc 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -3,6 +3,7 @@ package ussd import ( "bytes" "context" + "errors" "fmt" "path" "regexp" @@ -10,6 +11,7 @@ import ( "strings" "git.defalsify.org/vise.git/asm" + "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/db" @@ -27,8 +29,8 @@ var ( logg = logging.NewVanilla().WithDomain("ussdmenuhandler") scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") - okResponse *server.OKResponse - errResponse *server.ErrResponse + okResponse *api.OKResponse + errResponse *api.ErrResponse ) // FlagManager handles centralized flag management @@ -138,12 +140,19 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r } func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { + flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") + flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") okResponse, errResponse := h.accountService.CreateAccount() if errResponse != nil { - return nil + if !errResponse.Ok { + res.FlagSet = append(res.FlagSet, flag_api_call_error) + return nil + } + return errors.New(errResponse.Description) } trackingId := okResponse.Result["trackingId"].(string) publicKey := okResponse.Result["publicKey"].(string) + res.FlagReset = append(res.FlagReset, flag_api_call_error) data := map[utils.DataTyp]string{ utils.DATA_TRACKING_ID: trackingId, @@ -157,7 +166,6 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r return err } } - flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") res.FlagSet = append(res.FlagSet, flag_account_created) return nil @@ -546,6 +554,9 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b } okResponse, errResponse = h.accountService.TrackAccountStatus(string(publicKey)) if errResponse != nil { + if !errResponse.Ok { + res.FlagSet = append(res.FlagSet, flag_api_error) + } return res, err } res.FlagReset = append(res.FlagReset, flag_api_error) From 4beeb9986a3050dbe75c790ace6243a4c6e9b140 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 11:10:23 +0300 Subject: [PATCH 094/175] use importable api structs --- internal/mocks/servicemock.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 7796b77..0cf07c6 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -1,8 +1,8 @@ package mocks import ( - "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/models" + "github.com/grassrootseconomics/eth-custodial/pkg/api" "github.com/stretchr/testify/mock" ) @@ -11,10 +11,10 @@ type MockAccountService struct { mock.Mock } -func (m *MockAccountService) CreateAccount() (*server.OKResponse, *server.ErrResponse) { +func (m *MockAccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { args := m.Called() - okResponse, ok := args.Get(0).(*server.OKResponse) - errResponse, err := args.Get(1).(*server.ErrResponse) + okResponse, ok := args.Get(0).(*api.OKResponse) + errResponse, err := args.Get(1).(*api.ErrResponse) if ok { return okResponse, nil @@ -36,10 +36,10 @@ func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.Trac return args.Get(0).(*models.TrackStatusResponse), args.Error(1) } -func (m *MockAccountService) TrackAccountStatus(publicKey string) (*server.OKResponse, *server.ErrResponse) { +func (m *MockAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { args := m.Called(publicKey) - okResponse, ok := args.Get(0).(*server.OKResponse) - errResponse, err := args.Get(1).(*server.ErrResponse) + okResponse, ok := args.Get(0).(*api.OKResponse) + errResponse, err := args.Get(1).(*api.ErrResponse) if ok { return okResponse, nil } From 66d2e2e838b13aacc90cfd9314f51047dd18b387 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 11:10:42 +0300 Subject: [PATCH 095/175] add custodial api structs --- go.mod | 12 +++++++++--- go.sum | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1f67f34..e104186 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,33 @@ module git.grassecon.net/urdt/ussd -go 1.22.6 +go 1.23.0 + +toolchain go1.23.2 require ( git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb github.com/alecthomas/assert/v2 v2.2.2 github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 + ) +require github.com/grassrootseconomics/eth-custodial v1.3.0-beta // indirect + require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/repr v0.2.0 // indirect github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.9.0 github.com/x448/float16 v0.8.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + ) diff --git a/go.sum b/go.sum index 0eccb60..a3feb64 100644 --- a/go.sum +++ b/go.sum @@ -10,10 +10,14 @@ github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YV github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU= +github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -24,6 +28,8 @@ github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVY github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= From acd0af25c6e84322f3cb46ec7f43928d0f24affa Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 11:11:04 +0300 Subject: [PATCH 096/175] catch api call error --- services/registration/create_pin.vis | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index e0e330f..44d2cf1 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -1,4 +1,6 @@ LOAD create_account 0 +RELOAD create_account +CATCH api_failure flag_api_call_error 1 CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT From 0c08654df35dd7bac88c0ea24836e595ec0d5b20 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:42:57 +0300 Subject: [PATCH 097/175] move driver to testutil --- driver/groupdriver.go | 111 ------------------------------------------ 1 file changed, 111 deletions(-) delete mode 100644 driver/groupdriver.go diff --git a/driver/groupdriver.go b/driver/groupdriver.go deleted file mode 100644 index 68cb7e3..0000000 --- a/driver/groupdriver.go +++ /dev/null @@ -1,111 +0,0 @@ -package driver - -import ( - "encoding/json" - "log" - "os" - "regexp" -) - -type Step struct { - Input string `json:"input"` - ExpectedContent string `json:"expectedContent"` -} - -func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { - pattern := regexp.QuoteMeta(s.ExpectedContent) - re, err := regexp.Compile(pattern) - if err != nil { - return false, err - } - if re.Match([]byte(content)) { - return true, nil - } - return false, nil -} - -// Group represents a group of steps -type Group struct { - Name string `json:"name"` - Steps []Step `json:"steps"` -} - -type TestCase struct { - Name string - Input string - ExpectedContent string -} - -func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) { - pattern := regexp.QuoteMeta(s.ExpectedContent) - re, err := regexp.Compile(pattern) - if err != nil { - return false, err - } - // Check if the content matches the regex pattern - if re.Match(content) { - return true, nil - } - return false, nil -} - -// DataGroup represents the overall structure of the JSON. -type DataGroup struct { - Groups []Group `json:"groups"` -} - -type Session struct { - Name string `json:"name"` - Groups []Group `json:"groups"` -} - -func ReadData() []Session { - data, err := os.ReadFile("test_setup.json") - if err != nil { - log.Fatalf("Failed to read file: %v", err) - } - // Unmarshal JSON data - var sessions []Session - err = json.Unmarshal(data, &sessions) - if err != nil { - log.Fatalf("Failed to unmarshal JSON: %v", err) - } - - return sessions -} - -func FilterGroupsByName(groups []Group, name string) []Group { - var filteredGroups []Group - for _, group := range groups { - if group.Name == name { - filteredGroups = append(filteredGroups, group) - } - } - return filteredGroups -} - -func LoadTestGroups(filePath string) (DataGroup, error) { - var sessionsData DataGroup - data, err := os.ReadFile(filePath) - if err != nil { - return sessionsData, err - } - err = json.Unmarshal(data, &sessionsData) - return sessionsData, err -} - -func CreateTestCases(group DataGroup) []TestCase { - var tests []TestCase - for _, group := range group.Groups { - for _, step := range group.Steps { - // Create a test case for each group - tests = append(tests, TestCase{ - Name: group.Name, - Input: step.Input, - ExpectedContent: step.ExpectedContent, - }) - } - } - - return tests -} From 14455f65cb974a8e65a5b35f344af8914b0654e5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:43:24 +0300 Subject: [PATCH 098/175] move account service to testutil --- internal/handlers/server/accountservice.go | 59 ---------------------- 1 file changed, 59 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 5b71e6f..1ddf7e7 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -4,7 +4,6 @@ import ( "encoding/json" "io" "net/http" - "time" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" @@ -19,9 +18,6 @@ type AccountServiceInterface interface { type AccountService struct { } -type TestAccountService struct { -} - // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. // // Parameters: @@ -96,58 +92,3 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { } return &accountResp, nil } - -func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { - return &models.AccountResponse{ - Ok: true, - Result: struct { - CustodialId json.Number `json:"custodialId"` - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` - }{ - CustodialId: json.Number("182"), - PublicKey: "0x48ADca309b5085852207FAaf2816eD72B52F527C", - TrackingId: "28ebe84d-b925-472c-87ae-bbdfa1fb97be", - }, - }, nil -} - -func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { - - balanceResponse := &models.BalanceResponse{ - Ok: true, - Result: struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - }{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, - } - - return balanceResponse, nil -} - -func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { - trackResponse := &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - } - return trackResponse, nil -} From 3af943f77c81f44b0cee548611aa3a1f7a9863f2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:44:02 +0300 Subject: [PATCH 099/175] move account service tags switch to test tags package --- internal/testutil/offlinetest.go | 11 ----------- internal/testutil/onlinetest.go | 9 --------- 2 files changed, 20 deletions(-) delete mode 100644 internal/testutil/offlinetest.go delete mode 100644 internal/testutil/onlinetest.go diff --git a/internal/testutil/offlinetest.go b/internal/testutil/offlinetest.go deleted file mode 100644 index 476ade3..0000000 --- a/internal/testutil/offlinetest.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build !online - -package testutil - -import ( - "git.grassecon.net/urdt/ussd/internal/handlers/server" -) - -var ( - AccountService server.AccountServiceInterface = &server.TestAccountService{} -) diff --git a/internal/testutil/onlinetest.go b/internal/testutil/onlinetest.go deleted file mode 100644 index ddb5cf0..0000000 --- a/internal/testutil/onlinetest.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build online - -package testutil - -import "git.grassecon.net/urdt/ussd/internal/handlers/server" - -var ( - AccountService server.AccountServiceInterface -) From a904cdbf44f0f83f29e1151fe4d7e81f768d8abd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:45:06 +0300 Subject: [PATCH 100/175] move driver to testutil --- internal/testutil/driver/groupdriver.go | 111 ++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 internal/testutil/driver/groupdriver.go diff --git a/internal/testutil/driver/groupdriver.go b/internal/testutil/driver/groupdriver.go new file mode 100644 index 0000000..68cb7e3 --- /dev/null +++ b/internal/testutil/driver/groupdriver.go @@ -0,0 +1,111 @@ +package driver + +import ( + "encoding/json" + "log" + "os" + "regexp" +) + +type Step struct { + Input string `json:"input"` + ExpectedContent string `json:"expectedContent"` +} + +func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { + pattern := regexp.QuoteMeta(s.ExpectedContent) + re, err := regexp.Compile(pattern) + if err != nil { + return false, err + } + if re.Match([]byte(content)) { + return true, nil + } + return false, nil +} + +// Group represents a group of steps +type Group struct { + Name string `json:"name"` + Steps []Step `json:"steps"` +} + +type TestCase struct { + Name string + Input string + ExpectedContent string +} + +func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) { + pattern := regexp.QuoteMeta(s.ExpectedContent) + re, err := regexp.Compile(pattern) + if err != nil { + return false, err + } + // Check if the content matches the regex pattern + if re.Match(content) { + return true, nil + } + return false, nil +} + +// DataGroup represents the overall structure of the JSON. +type DataGroup struct { + Groups []Group `json:"groups"` +} + +type Session struct { + Name string `json:"name"` + Groups []Group `json:"groups"` +} + +func ReadData() []Session { + data, err := os.ReadFile("test_setup.json") + if err != nil { + log.Fatalf("Failed to read file: %v", err) + } + // Unmarshal JSON data + var sessions []Session + err = json.Unmarshal(data, &sessions) + if err != nil { + log.Fatalf("Failed to unmarshal JSON: %v", err) + } + + return sessions +} + +func FilterGroupsByName(groups []Group, name string) []Group { + var filteredGroups []Group + for _, group := range groups { + if group.Name == name { + filteredGroups = append(filteredGroups, group) + } + } + return filteredGroups +} + +func LoadTestGroups(filePath string) (DataGroup, error) { + var sessionsData DataGroup + data, err := os.ReadFile(filePath) + if err != nil { + return sessionsData, err + } + err = json.Unmarshal(data, &sessionsData) + return sessionsData, err +} + +func CreateTestCases(group DataGroup) []TestCase { + var tests []TestCase + for _, group := range group.Groups { + for _, step := range group.Steps { + // Create a test case for each group + tests = append(tests, TestCase{ + Name: group.Name, + Input: step.Input, + ExpectedContent: step.ExpectedContent, + }) + } + } + + return tests +} From 961d8d1a5ce11964430ec50527d7cd529497c53e Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:46:29 +0300 Subject: [PATCH 101/175] update package import --- menutraversal_test/menu_traversal_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index 5eb1ef8..7391e5b 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -9,8 +9,8 @@ import ( "regexp" "testing" - "git.grassecon.net/urdt/ussd/driver" "git.grassecon.net/urdt/ussd/internal/testutil" + "git.grassecon.net/urdt/ussd/internal/testutil/driver" "github.com/gofrs/uuid" ) From 4a9ef6b5f24cba95c583991b5c35d79016f93cb3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:47:00 +0300 Subject: [PATCH 102/175] move test account service to testutil --- .../testservice/TestAccountService.go | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 internal/testutil/testservice/TestAccountService.go diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go new file mode 100644 index 0000000..40c3886 --- /dev/null +++ b/internal/testutil/testservice/TestAccountService.go @@ -0,0 +1,65 @@ +package testservice + +import ( + "encoding/json" + "time" + + "git.grassecon.net/urdt/ussd/internal/models" +) + +type TestAccountService struct { +} + +func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { + return &models.AccountResponse{ + Ok: true, + Result: struct { + CustodialId json.Number `json:"custodialId"` + PublicKey string `json:"publicKey"` + TrackingId string `json:"trackingId"` + }{ + CustodialId: json.Number("182"), + PublicKey: "0x48ADca309b5085852207FAaf2816eD72B52F527C", + TrackingId: "28ebe84d-b925-472c-87ae-bbdfa1fb97be", + }, + }, nil +} + +func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { + balanceResponse := &models.BalanceResponse{ + Ok: true, + Result: struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` + }{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), + }, + } + + return balanceResponse, nil +} + +func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { + trackResponse := &models.TrackStatusResponse{ + Ok: true, + Result: struct { + Transaction struct { + CreatedAt time.Time "json:\"createdAt\"" + Status string "json:\"status\"" + TransferValue json.Number "json:\"transferValue\"" + TxHash string "json:\"txHash\"" + TxType string "json:\"txType\"" + } + }{ + Transaction: models.Transaction{ + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", + }, + }, + } + return trackResponse, nil +} From 549d09890b86d73b6ac9f6f4bad34107df5e2274 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:47:25 +0300 Subject: [PATCH 103/175] update imports --- internal/testutil/TestEngine.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index 2432a3f..ca48655 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -13,6 +13,8 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/internal/testutil/testservice" + "git.grassecon.net/urdt/ussd/internal/testutil/testtag" testdataloader "github.com/peteole/testdata-loader" ) @@ -79,12 +81,12 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { os.Exit(1) } - if AccountService == nil { - AccountService = &server.AccountService{} + if testtag.AccountService == nil { + testtag.AccountService = &server.AccountService{} } - switch AccountService.(type) { - case *server.TestAccountService: + switch testtag.AccountService.(type) { + case *testservice.TestAccountService: go func() { eventChannel <- false }() @@ -97,7 +99,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { panic("Unknown account service type") } - hl, err := lhs.GetHandler(AccountService) + hl, err := lhs.GetHandler(testtag.AccountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) From f3a5178de70d4a6290b1fc2b3746174581a54341 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:47:43 +0300 Subject: [PATCH 104/175] update imports --- internal/handlers/ussd/menuhandler_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 38c468c..42e97fd 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -15,9 +15,10 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" - "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/mocks" "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/internal/testutil/testservice" + "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" testdataloader "github.com/peteole/testdata-loader" @@ -31,7 +32,7 @@ var ( func TestNewHandlers(t *testing.T) { fm, err := NewFlagManager(flagsPath) - accountService := server.TestAccountService{} + accountService := testservice.TestAccountService{} if err != nil { t.Logf(err.Error()) } From eaac771722225641a28271502b10267094763936 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:48:24 +0300 Subject: [PATCH 105/175] move into testtag package --- internal/testutil/testtag/offlinetest.go | 12 ++++++++++++ internal/testutil/testtag/onlinetest.go | 9 +++++++++ 2 files changed, 21 insertions(+) create mode 100644 internal/testutil/testtag/offlinetest.go create mode 100644 internal/testutil/testtag/onlinetest.go diff --git a/internal/testutil/testtag/offlinetest.go b/internal/testutil/testtag/offlinetest.go new file mode 100644 index 0000000..70e2e80 --- /dev/null +++ b/internal/testutil/testtag/offlinetest.go @@ -0,0 +1,12 @@ +// +build !online + +package testtag + +import ( + "git.grassecon.net/urdt/ussd/internal/handlers/server" + accountservice "git.grassecon.net/urdt/ussd/internal/testutil/testservice" +) + +var ( + AccountService server.AccountServiceInterface = &accountservice.TestAccountService{} +) diff --git a/internal/testutil/testtag/onlinetest.go b/internal/testutil/testtag/onlinetest.go new file mode 100644 index 0000000..92cbb14 --- /dev/null +++ b/internal/testutil/testtag/onlinetest.go @@ -0,0 +1,9 @@ +// +build online + +package testtag + +import "git.grassecon.net/urdt/ussd/internal/handlers/server" + +var ( + AccountService server.AccountServiceInterface +) From 5909659fa96708b52a1e21a244b7794002c37090 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 21 Oct 2024 16:52:07 +0300 Subject: [PATCH 106/175] Use dynamic balance --- menutraversal_test/group_test.json | 44 +++++++++++------------ menutraversal_test/menu_traversal_test.go | 38 ++++++++++++++++++-- menutraversal_test/test_setup.json | 8 ++--- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 203ff08..8b765f5 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -5,7 +5,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -33,7 +33,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -42,7 +42,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -70,7 +70,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -79,7 +79,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -116,7 +116,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -125,7 +125,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -162,7 +162,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -171,7 +171,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -203,7 +203,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -212,7 +212,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -244,7 +244,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] @@ -254,7 +254,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -286,7 +286,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -295,7 +295,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -327,7 +327,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -336,7 +336,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -368,7 +368,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -377,7 +377,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -409,7 +409,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] }, @@ -418,7 +418,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", @@ -446,7 +446,7 @@ }, { "input": "0", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" } ] } diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index 5eb1ef8..a32e17a 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -43,6 +43,17 @@ func extractPublicKey(response []byte) string { return "" } +// Extracts the balance value from the engine response. +func extractBalance(response []byte) string { + // Regex to match "Balance: " followed by a newline + re := regexp.MustCompile(`(?m)^Balance:\s+(\d+(\.\d+)?)\s+([A-Z]+)`) + match := re.FindSubmatch(response) + if match != nil && len(match) > 0 { + return string(match[1]) + " " + string(match[3]) // " " + } + return "" +} + func TestMain(m *testing.M) { sessionID = GenerateSessionId() defer func() { @@ -154,6 +165,12 @@ func TestMainMenuHelp(t *testing.T) { } b := w.Bytes() + balance := extractBalance(b) + + expectedContent := []byte(step.ExpectedContent) + expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) + + step.ExpectedContent = string(expectedContent) match, err := step.MatchesExpectedContent(b) if err != nil { t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) @@ -189,6 +206,12 @@ func TestMainMenuQuit(t *testing.T) { } b := w.Bytes() + balance := extractBalance(b) + + expectedContent := []byte(step.ExpectedContent) + expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) + + step.ExpectedContent = string(expectedContent) match, err := step.MatchesExpectedContent(b) if err != nil { t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) @@ -225,8 +248,13 @@ func TestMyAccount_MyAddress(t *testing.T) { } b := w.Bytes() + balance := extractBalance(b) publicKey := extractPublicKey(b) - expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) + + expectedContent := []byte(step.ExpectedContent) + expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) + expectedContent = bytes.Replace(expectedContent, []byte("{public_key}"), []byte(publicKey), -1) + step.ExpectedContent = string(expectedContent) match, err := step.MatchesExpectedContent(b) if err != nil { @@ -265,6 +293,13 @@ func TestGroups(t *testing.T) { t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err) } b := w.Bytes() + balance := extractBalance(b) + + expectedContent := []byte(tt.ExpectedContent) + expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) + + tt.ExpectedContent = string(expectedContent) + match, err := tt.MatchesExpectedContent(b) if err != nil { t.Fatalf("Error compiling regex for step '%s': %v", tt.Input, err) @@ -272,7 +307,6 @@ func TestGroups(t *testing.T) { if !match { t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b) } - }) } } diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 56c0278..619744b 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -57,7 +57,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "1", @@ -106,7 +106,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "4", @@ -119,7 +119,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "9", @@ -132,7 +132,7 @@ "steps": [ { "input": "", - "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" }, { "input": "3", From 9c75942b0b8d3533664eb6a8816e5f999fd6ed07 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 21 Oct 2024 16:54:16 +0300 Subject: [PATCH 107/175] chore: remove commented code --- internal/handlers/ussd/menuhandler_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index d78f526..3b4b00f 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -75,7 +75,6 @@ func TestCreateAccount(t *testing.T) { } // Create required mocks flag_account_created, err := fm.GetFlag("flag_account_created") - //flag_api_call_error, err := fm.GetFlag("flag_api_call_error,") flag_api_call_error, _ := fm.GetFlag("flag_api_call_error") if err != nil { t.Logf(err.Error()) @@ -102,7 +101,7 @@ func TestCreateAccount(t *testing.T) { }, }, expectedResult: resource.Result{ - FlagSet: []uint32{flag_account_created}, + FlagSet: []uint32{flag_account_created}, FlagReset: []uint32{flag_api_call_error}, }, }, From 02cb75f97a61150830bfbdcb58860c7b52c96c68 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 21 Oct 2024 17:14:59 +0300 Subject: [PATCH 108/175] increase the size to resolve sink error --- services/registration/confirm_create_pin.vis | 2 +- services/registration/create_pin.vis | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/registration/confirm_create_pin.vis b/services/registration/confirm_create_pin.vis index 1a3173c..02279dc 100644 --- a/services/registration/confirm_create_pin.vis +++ b/services/registration/confirm_create_pin.vis @@ -1,4 +1,4 @@ -LOAD save_temporary_pin 0 +LOAD save_temporary_pin 6 HALT LOAD verify_create_pin 8 INCMP account_creation * diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index c76e1bf..40989ec 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -2,7 +2,7 @@ LOAD create_account 0 CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT -LOAD save_temporary_pin 0 +LOAD save_temporary_pin 6 RELOAD save_temporary_pin CATCH . flag_incorrect_pin 1 INCMP quit 0 From 0547bc7e5fe0b2c35d9973553b05d48d853fd082 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 22 Oct 2024 14:24:22 +0300 Subject: [PATCH 109/175] added send menu test with dynamic max_amount --- menutraversal_test/menu_traversal_test.go | 57 +++++++++++++++++++++++ menutraversal_test/test_setup.json | 8 ++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index a32e17a..8d028c9 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -54,6 +54,17 @@ func extractBalance(response []byte) string { return "" } +// Extracts the Maximum amount value from the engine response. +func extractMaxAmount(response []byte) string { + // Regex to match "Maximum amount: " followed by a newline + re := regexp.MustCompile(`(?m)^Maximum amount:\s+(\d+(\.\d+)?)\s+([A-Z]+)`) + match := re.FindSubmatch(response) + if match != nil && len(match) > 0 { + return string(match[1]) + " " + string(match[3]) // " " + } + return "" +} + func TestMain(m *testing.M) { sessionID = GenerateSessionId() defer func() { @@ -268,6 +279,52 @@ func TestMyAccount_MyAddress(t *testing.T) { } } +func TestMainMenuSend(t *testing.T) { + en, fn, _ := testutil.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "send_with_invalid_inputs") + for _, group := range groups { + for _, step := range group.Steps { + cont, err := en.Exec(ctx, []byte(step.Input)) + if err != nil { + t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) + return + } + if !cont { + break + } + w := bytes.NewBuffer(nil) + if _, err := en.Flush(ctx, w); err != nil { + t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) + } + + b := w.Bytes() + balance := extractBalance(b) + max_amount := extractMaxAmount(b) + publicKey := extractPublicKey(b) + + expectedContent := []byte(step.ExpectedContent) + expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) + expectedContent = bytes.Replace(expectedContent, []byte("{max_amount}"), []byte(max_amount), -1) + expectedContent = bytes.Replace(expectedContent, []byte("{public_key}"), []byte(publicKey), -1) + + step.ExpectedContent = string(expectedContent) + match, err := step.MatchesExpectedContent(b) + if err != nil { + t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) + } + if !match { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + + func TestGroups(t *testing.T) { groups, err := driver.LoadTestGroups(groupTestFile) if err != nil { diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 619744b..736d4de 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -73,15 +73,15 @@ }, { "input": "065656", - "expectedContent": "Maximum amount: 0.003 CELO\nEnter amount:\n0:Back" + "expectedContent": "{max_amount}\nEnter amount:\n0:Back" }, { - "input": "0.1", - "expectedContent": "Amount 0.1 is invalid, please try again:\n1:retry\n9:Quit" + "input": "10000000", + "expectedContent": "Amount 10000000 is invalid, please try again:\n1:retry\n9:Quit" }, { "input": "1", - "expectedContent": "Maximum amount: 0.003 CELO\nEnter amount:\n0:Back" + "expectedContent": "{max_amount}\nEnter amount:\n0:Back" }, { "input": "0.001", From f37ec13c757fce0e3cc3db308e95fd695b9b396f Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 22 Oct 2024 16:31:29 +0300 Subject: [PATCH 110/175] polish code --- menutraversal_test/menu_traversal_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index 8d028c9..2b2a093 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -48,7 +48,7 @@ func extractBalance(response []byte) string { // Regex to match "Balance: " followed by a newline re := regexp.MustCompile(`(?m)^Balance:\s+(\d+(\.\d+)?)\s+([A-Z]+)`) match := re.FindSubmatch(response) - if match != nil && len(match) > 0 { + if match != nil { return string(match[1]) + " " + string(match[3]) // " " } return "" @@ -59,7 +59,7 @@ func extractMaxAmount(response []byte) string { // Regex to match "Maximum amount: " followed by a newline re := regexp.MustCompile(`(?m)^Maximum amount:\s+(\d+(\.\d+)?)\s+([A-Z]+)`) match := re.FindSubmatch(response) - if match != nil && len(match) > 0 { + if match != nil { return string(match[1]) + " " + string(match[3]) // " " } return "" @@ -324,7 +324,6 @@ func TestMainMenuSend(t *testing.T) { } } - func TestGroups(t *testing.T) { groups, err := driver.LoadTestGroups(groupTestFile) if err != nil { From e05dbb4885d033c398713adffbea31c36e8a2279 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 22 Oct 2024 20:36:58 +0300 Subject: [PATCH 111/175] check for back option --- internal/handlers/ussd/menuhandler.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 36d1ad5..2c4a4a5 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -27,6 +27,7 @@ var ( logg = logging.NewVanilla().WithDomain("ussdmenuhandler") scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") + backOption = []byte("0") ) // FlagManager handles centralized flag management @@ -326,6 +327,9 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) return res, fmt.Errorf("missing session") } if len(input) > 0 { + if bytes.Equal(input, backOption) { + return res, nil + } firstName := string(input) store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)) @@ -345,8 +349,10 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { + if bytes.Equal(input, backOption) { + return res, nil + } familyName := string(input) store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)) @@ -389,8 +395,10 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { + if bytes.Equal(input, backOption) { + return res, nil + } location := string(input) store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location)) @@ -411,7 +419,9 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re if !ok { return res, fmt.Errorf("missing session") } - + if bytes.Equal(input, backOption) { + return res, nil + } gender := strings.Split(symbol, "_")[1] store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) From 306666a15e321ea3c9b81ed9496d1634ccfbb479 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 22 Oct 2024 20:37:30 +0300 Subject: [PATCH 112/175] load and reload after halt --- services/registration/enter_familyname.vis | 3 +-- services/registration/enter_location.vis | 2 +- services/registration/enter_name.vis | 6 +----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/services/registration/enter_familyname.vis b/services/registration/enter_familyname.vis index b9fe7b0..5a684ed 100644 --- a/services/registration/enter_familyname.vis +++ b/services/registration/enter_familyname.vis @@ -1,9 +1,8 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH profile_update_success flag_allow_update 1 -LOAD save_familyname 0 -RELOAD save_familyname MOUT back 0 HALT +LOAD save_familyname 0 RELOAD save_familyname INCMP _ 0 INCMP pin_entry * diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index fdd29ce..c8da2dd 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,8 +1,8 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH profile_update_success flag_allow_update 1 -LOAD save_location 0 MOUT back 0 HALT +LOAD save_location 0 RELOAD save_location INCMP _ 0 INCMP pin_entry * diff --git a/services/registration/enter_name.vis b/services/registration/enter_name.vis index 563577e..799b2a1 100644 --- a/services/registration/enter_name.vis +++ b/services/registration/enter_name.vis @@ -1,12 +1,8 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH profile_update_success flag_allow_update 1 -LOAD save_firstname 0 -RELOAD save_firstname MOUT back 0 HALT +LOAD save_firstname 0 RELOAD save_firstname INCMP _ 0 INCMP pin_entry * - - - From b9a63f3c6feb15b9d895f18aee4861e333f0fc0c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 22 Oct 2024 21:30:48 +0300 Subject: [PATCH 113/175] merge dep --- go.mod | 15 +++++++++++++-- go.sum | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 7654709..1225673 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,20 @@ require ( ) -require github.com/joho/godotenv v1.5.1 // indirect +require github.com/joho/godotenv v1.5.1 -require github.com/grassrootseconomics/eth-custodial v1.3.0-beta // indirect +require ( + github.com/grassrootseconomics/eth-custodial v1.3.0-beta + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect +) require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 1d1de2d..d566e2c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihcwMjDKzvUFC6t2zGNb7MDW+l/ACGlSAN1N8Y= -git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= -git.defalsify.org/vise.git v0.2.0 h1:X2ZgiGRq4C+9qOlDMP0b/oE5QHjVQNT4aEFZB88ST0Q= -git.defalsify.org/vise.git v0.2.0/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= @@ -12,8 +8,8 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= @@ -26,25 +22,51 @@ github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qf github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc= gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 3179ec1f62d46eda15240866d007131b5e11b755 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 22 Oct 2024 21:35:52 +0300 Subject: [PATCH 114/175] add local track endpoint --- config/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index dba0da7..43466c3 100644 --- a/config/config.go +++ b/config/config.go @@ -6,11 +6,13 @@ var ( CreateAccountURL string TrackStatusURL string BalanceURL string + TrackURL string ) // LoadConfig initializes the configuration values after environment variables are loaded. func LoadConfig() { - CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "https://custodial.sarafu.africa/api/account/create") + CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create") TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") + TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status") } From 5f2c6cce163e0605f57179d9c2fbd7bc9cf2619a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 22 Oct 2024 21:36:28 +0300 Subject: [PATCH 115/175] add required track endpoint --- .env.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 97d3317..ab370a7 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,7 @@ DB_SSLMODE=disable DB_TIMEZONE=Africa/Nairobi #External API Calls -CREATE_ACCOUNT_URL=https://custodial.sarafu.africa/api/account/create +CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ +TRACK_URL=http://localhost:5003/api/v2/account/status From 5f1ee396d84d069c25a5ca5d71bccbad855e373f Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 06:35:19 +0300 Subject: [PATCH 116/175] Fix PIN being requested twice --- services/registration/my_vouchers.vis | 2 ++ services/registration/view_voucher.vis | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/services/registration/my_vouchers.vis b/services/registration/my_vouchers.vis index 9702573..b59441a 100644 --- a/services/registration/my_vouchers.vis +++ b/services/registration/my_vouchers.vis @@ -1,3 +1,5 @@ +LOAD reset_account_authorized 16 +RELOAD reset_account_authorized MOUT select_voucher 1 MOUT voucher_details 2 MOUT back 0 diff --git a/services/registration/view_voucher.vis b/services/registration/view_voucher.vis index ee8bf85..1480099 100644 --- a/services/registration/view_voucher.vis +++ b/services/registration/view_voucher.vis @@ -1,4 +1,3 @@ -RELOAD view_voucher MAP view_voucher MOUT back 0 MOUT quit 9 From 1e6cf6a33a94a6ebbe8a212b0db1c231ba7fe7ec Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 23 Oct 2024 11:08:51 +0300 Subject: [PATCH 117/175] run mod tidy --- go.mod | 13 ++++++++++++- go.sum | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 38be305..3349bf8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,18 @@ require ( gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) -require github.com/joho/godotenv v1.5.1 // indirect +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.0 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/joho/godotenv v1.5.1 + github.com/kr/text v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect +) require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 2abec41..ef40172 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihcwMjDKzvUFC6t2zGNb7MDW+l/ACGlSAN1N8Y= -git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= -git.defalsify.org/vise.git v0.2.0 h1:X2ZgiGRq4C+9qOlDMP0b/oE5QHjVQNT4aEFZB88ST0Q= -git.defalsify.org/vise.git v0.2.0/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= @@ -12,6 +8,8 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= @@ -22,23 +20,50 @@ github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qf github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.0 h1:FG6VLIdzvAPhnYqP14sQ2xhFLkiUQHCs6ySqO91kF4g= +github.com/jackc/pgx/v5 v5.7.0/go.mod h1:awP1KNnjylvpxHuHP63gzjhnGkI1iw+PMoIwvoleN/8= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc= gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From fb32dde136d2587b55638fece25f78a455750d57 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 23 Oct 2024 12:45:10 +0300 Subject: [PATCH 118/175] run go mod tidy --- go.mod | 13 ++++++++++++- go.sum | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 38be305..3349bf8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,18 @@ require ( gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) -require github.com/joho/godotenv v1.5.1 // indirect +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.0 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/joho/godotenv v1.5.1 + github.com/kr/text v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect +) require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 2abec41..ef40172 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihcwMjDKzvUFC6t2zGNb7MDW+l/ACGlSAN1N8Y= -git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= -git.defalsify.org/vise.git v0.2.0 h1:X2ZgiGRq4C+9qOlDMP0b/oE5QHjVQNT4aEFZB88ST0Q= -git.defalsify.org/vise.git v0.2.0/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M= git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= @@ -12,6 +8,8 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= @@ -22,23 +20,50 @@ github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qf github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.0 h1:FG6VLIdzvAPhnYqP14sQ2xhFLkiUQHCs6ySqO91kF4g= +github.com/jackc/pgx/v5 v5.7.0/go.mod h1:awP1KNnjylvpxHuHP63gzjhnGkI1iw+PMoIwvoleN/8= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc= gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From b41e52af63c845386946845238e99548b78bc0bc Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 23 Oct 2024 12:45:54 +0300 Subject: [PATCH 119/175] pass context.Context --- internal/handlers/server/accountservice.go | 19 ++++++++++--------- internal/handlers/ussd/menuhandler.go | 12 ++++++------ internal/mocks/servicemock.go | 8 +++++--- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 5b71e6f..f86c2fb 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "io" "net/http" @@ -11,9 +12,9 @@ import ( ) type AccountServiceInterface interface { - CheckBalance(publicKey string) (*models.BalanceResponse, error) - CreateAccount() (*models.AccountResponse, error) - CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) + CheckBalance(publicKey string, ctx context.Context) (*models.BalanceResponse, error) + CreateAccount(ctx context.Context) (*models.AccountResponse, error) + CheckAccountStatus(trackingId string, ctx context.Context) (*models.TrackStatusResponse, error) } type AccountService struct { @@ -33,7 +34,7 @@ type TestAccountService struct { // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { +func (as *AccountService) CheckAccountStatus(trackingId string, ctx context.Context) (*models.TrackStatusResponse, error) { resp, err := http.Get(config.TrackStatusURL + trackingId) if err != nil { return nil, err @@ -55,7 +56,7 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackSt // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. -func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { +func (as *AccountService) CheckBalance(publicKey string, ctx context.Context) (*models.BalanceResponse, error) { resp, err := http.Get(config.BalanceURL + publicKey) if err != nil { return nil, err @@ -79,7 +80,7 @@ func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceRespons // If there is an error during the request or processing, this will be nil. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { +func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountResponse, error) { resp, err := http.Post(config.CreateAccountURL, "application/json", nil) if err != nil { return nil, err @@ -97,7 +98,7 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return &accountResp, nil } -func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { +func (tas *TestAccountService) CreateAccount(ctx context.Context) (*models.AccountResponse, error) { return &models.AccountResponse{ Ok: true, Result: struct { @@ -112,7 +113,7 @@ func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) }, nil } -func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { +func (tas *TestAccountService) CheckBalance(publicKey string, ctx context.Context) (*models.BalanceResponse, error) { balanceResponse := &models.BalanceResponse{ Ok: true, @@ -128,7 +129,7 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe return balanceResponse, nil } -func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { +func (tas *TestAccountService) CheckAccountStatus(trackingId string, ctx context.Context) (*models.TrackStatusResponse, error) { trackResponse := &models.TrackStatusResponse{ Ok: true, Result: struct { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 36d1ad5..03351c0 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -136,7 +136,7 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r } func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { - accountResp, err := h.accountService.CreateAccount() + accountResp, err := h.accountService.CreateAccount(ctx) data := map[utils.DataTyp]string{ utils.DATA_TRACKING_ID: accountResp.Result.TrackingId, utils.DATA_PUBLIC_KEY: accountResp.Result.PublicKey, @@ -547,7 +547,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b return res, err } - accountStatus, err := h.accountService.CheckAccountStatus(string(trackingId)) + accountStatus, err := h.accountService.CheckAccountStatus(string(trackingId),ctx) if err != nil { fmt.Println("Error checking account status:", err) return res, err @@ -656,7 +656,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - balanceResponse, err := h.accountService.CheckBalance(string(publicKey)) + balanceResponse, err := h.accountService.CheckBalance(string(publicKey),ctx) if err != nil { return res, nil } @@ -688,7 +688,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, err } - balanceResponse, err := h.accountService.CheckBalance(string(publicKey)) + balanceResponse, err := h.accountService.CheckBalance(string(publicKey),ctx) if err != nil { return res, nil } @@ -806,7 +806,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res store := h.userdataStore publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - balanceResp, err := h.accountService.CheckBalance(string(publicKey)) + balanceResp, err := h.accountService.CheckBalance(string(publicKey),ctx) if err != nil { return res, nil } @@ -836,7 +836,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) amountStr := string(input) - balanceRes, err := h.accountService.CheckBalance(string(publicKey)) + balanceRes, err := h.accountService.CheckBalance(string(publicKey),ctx) balanceStr := balanceRes.Result.Balance if !balanceRes.Ok { diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index d828045..8c2b4ea 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -1,6 +1,8 @@ package mocks import ( + "context" + "git.grassecon.net/urdt/ussd/internal/models" "github.com/stretchr/testify/mock" ) @@ -10,17 +12,17 @@ type MockAccountService struct { mock.Mock } -func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { +func (m *MockAccountService) CreateAccount(ctx context.Context) (*models.AccountResponse, error) { args := m.Called() return args.Get(0).(*models.AccountResponse), args.Error(1) } -func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { +func (m *MockAccountService) CheckBalance(publicKey string,ctx context.Context) (*models.BalanceResponse, error) { args := m.Called(publicKey) return args.Get(0).(*models.BalanceResponse), args.Error(1) } -func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { +func (m *MockAccountService) CheckAccountStatus(trackingId string,ctx context.Context) (*models.TrackStatusResponse, error) { args := m.Called(trackingId) return args.Get(0).(*models.TrackStatusResponse), args.Error(1) } \ No newline at end of file From 176473aa264cc0c353e2a78f0c69f8cb3bc31aa2 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 13:54:42 +0300 Subject: [PATCH 120/175] Rename prefix to vouchers --- internal/handlers/ussd/menuhandler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 621a43e..d464ca3 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1068,7 +1068,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) // process voucher data voucherSymbolList, voucherBalanceList := ProcessVouchers(vouchersResp.Result.Holdings) - prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) + prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) err = prefixdb.Put(ctx, []byte("sym"), []byte(voucherSymbolList)) if err != nil { return res, nil @@ -1108,7 +1108,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) // Read vouchers from the store store := h.userdataStore - prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) + prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) voucherData, err := prefixdb.Get(ctx, []byte("sym")) if err != nil { @@ -1139,7 +1139,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - prefixdb := storage.NewSubPrefixDb(store, []byte("pfx")) + prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) // Retrieve the voucher symbol list voucherSymbolList, err := prefixdb.Get(ctx, []byte("sym")) From 4011597d9c9d6653c35cb569970aba62066b59e9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:02:13 +0300 Subject: [PATCH 121/175] Check specific db error --- internal/handlers/ussd/menuhandler.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d464ca3..009d3cc 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -640,8 +640,12 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( // get the active sym and active balance activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) if err != nil { - res.Content = "0.00" - return res, nil + if db.IsNotFound(err) { + res.Content = "0.00" + return res, nil + } + + return res, err } activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) From a553731f02f3fb8bb53150a6982eca1629372a9b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 17:45:41 +0300 Subject: [PATCH 122/175] Cleaned up code --- internal/handlers/ussd/menuhandler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 009d3cc..f57b4e7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1004,7 +1004,6 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by if err != nil { if db.IsNotFound(err) { publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - if err != nil { return res, nil } From 9bc9d04a49c591869692b8c59fd4ed0aa8e81087 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 10:00:38 +0300 Subject: [PATCH 123/175] use errresponse and okresponse for deserialization --- internal/handlers/server/accountservice.go | 97 ++++++++-------------- 1 file changed, 34 insertions(+), 63 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 39d1c66..6ae9630 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -2,6 +2,7 @@ package server import ( "encoding/json" + "errors" "fmt" "io" "net/http" @@ -12,16 +13,17 @@ import ( "github.com/grassrootseconomics/eth-custodial/pkg/api" ) -type ApiResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` -} +var ( + okResponse api.OKResponse + errResponse api.ErrResponse + EMPTY_RESPONSE = 0 +) type AccountServiceInterface interface { CheckBalance(publicKey string) (*models.BalanceResponse, error) - CreateAccount() (*api.OKResponse, *api.ErrResponse) + CreateAccount() (*api.OKResponse, error) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) - TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) + TrackAccountStatus(publicKey string) (*api.OKResponse, error) } type AccountService struct { @@ -60,58 +62,42 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackSt } -func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { - var errResponse api.ErrResponse - var okResponse api.OKResponse +func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { var err error // Construct the URL with the path parameter url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) req, err := http.NewRequest("GET", url, nil) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } - // Set headers + req.Header.Set("Content-Type", "application/json") req.Header.Set("X-GE-KEY", "xd") - // Send the request resp, err := http.DefaultClient.Do(req) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } defer resp.Body.Close() - // Read the response body body, err := io.ReadAll(resp.Body) if err != nil { errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } - - // Step 2: Unmarshal into the generic struct - var apiResponse ApiResponse - err = json.Unmarshal([]byte(body), &apiResponse) + err = json.Unmarshal([]byte(body), &okResponse) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse - } - if apiResponse.Ok { - err = json.Unmarshal([]byte(body), &okResponse) - if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse - } - return &okResponse, nil - } else { err := json.Unmarshal([]byte(body), &errResponse) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } - return nil, &errResponse + return nil, errors.New(errResponse.Description) } + if len(okResponse.Result) == EMPTY_RESPONSE { + return nil, errors.New("Empty api result") + } + return &okResponse, nil + } // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. @@ -141,17 +127,13 @@ func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceRespons // If there is an error during the request or processing, this will be nil. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -func (as *AccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { - - var errResponse api.ErrResponse - var okResponse api.OKResponse +func (as *AccountService) CreateAccount() (*api.OKResponse, error) { var err error // Create a new request req, err := http.NewRequest("POST", config.CreateAccountURL, nil) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-GE-KEY", "xd") @@ -159,38 +141,29 @@ func (as *AccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { resp, err := http.DefaultClient.Do(req) if err != nil { errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } - var apiResponse ApiResponse - err = json.Unmarshal([]byte(body), &apiResponse) + err = json.Unmarshal([]byte(body), &okResponse) if err != nil { - return nil, &errResponse - } - if apiResponse.Ok { - err = json.Unmarshal([]byte(body), &okResponse) - if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse - } - return &okResponse, nil - } else { err := json.Unmarshal([]byte(body), &errResponse) if err != nil { - errResponse.Description = err.Error() - return nil, &errResponse + return nil, err } - return nil, &errResponse + return nil, errors.New(errResponse.Description) } + if len(okResponse.Result) == EMPTY_RESPONSE { + return nil, errors.New("Empty api result") + } + return &okResponse, nil } -func (tas *TestAccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { +func (tas *TestAccountService) CreateAccount() (*api.OKResponse, error) { return &api.OKResponse{ Ok: true, Description: "Account creation request received successfully", @@ -200,7 +173,6 @@ func (tas *TestAccountService) CreateAccount() (*api.OKResponse, *api.ErrRespons } func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { - balanceResponse := &models.BalanceResponse{ Ok: true, Result: struct { @@ -211,11 +183,10 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe Nonce: json.Number("0"), }, } - return balanceResponse, nil } -func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { +func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { return &api.OKResponse{ Ok: true, Description: "Account creation succeeded", From 08e709f1b349f1bbe51c829c852977d0eab3743c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 10:02:15 +0300 Subject: [PATCH 124/175] check for error responses --- internal/handlers/ussd/menuhandler.go | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 0fc30cc..e7f52eb 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -3,7 +3,6 @@ package ussd import ( "bytes" "context" - "errors" "fmt" "path" "regexp" @@ -141,24 +140,17 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") - flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") - okResponse, errResponse := h.accountService.CreateAccount() - if errResponse != nil { - if !errResponse.Ok { - res.FlagSet = append(res.FlagSet, flag_api_call_error) - return nil - } - return errors.New(errResponse.Description) + okResponse, err := h.accountService.CreateAccount() + if err != nil { + return err } trackingId := okResponse.Result["trackingId"].(string) publicKey := okResponse.Result["publicKey"].(string) - res.FlagReset = append(res.FlagReset, flag_api_call_error) data := map[utils.DataTyp]string{ utils.DATA_TRACKING_ID: trackingId, utils.DATA_PUBLIC_KEY: publicKey, } - for key, value := range data { store := h.userdataStore err := store.WriteEntry(ctx, sessionId, key, []byte(value)) @@ -552,11 +544,9 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b if err != nil { return res, err } - okResponse, errResponse = h.accountService.TrackAccountStatus(string(publicKey)) - if errResponse != nil { - if !errResponse.Ok { - res.FlagSet = append(res.FlagSet, flag_api_error) - } + okResponse, err = h.accountService.TrackAccountStatus(string(publicKey)) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) return res, err } res.FlagReset = append(res.FlagReset, flag_api_error) From 308ca99fb082d646aa34aae5610841232fbb8df4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 10:02:32 +0300 Subject: [PATCH 125/175] remove reload account creation --- services/registration/create_pin.vis | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index 44d2cf1..e0e330f 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -1,6 +1,4 @@ LOAD create_account 0 -RELOAD create_account -CATCH api_failure flag_api_call_error 1 CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT From d74a4cc33ba5fb1c04f2a6b1a03062ea1f8cc0e7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 10:10:03 +0300 Subject: [PATCH 126/175] update service mock method signatures --- internal/mocks/servicemock.go | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 0cf07c6..0b56314 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -11,19 +11,9 @@ type MockAccountService struct { mock.Mock } -func (m *MockAccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { +func (m *MockAccountService) CreateAccount() (*api.OKResponse, error) { args := m.Called() - okResponse, ok := args.Get(0).(*api.OKResponse) - errResponse, err := args.Get(1).(*api.ErrResponse) - - if ok { - return okResponse, nil - } - - if err { - return nil, errResponse - } - return nil, nil + return args.Get(0).(*api.OKResponse), args.Error(1) } func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { @@ -36,15 +26,7 @@ func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.Trac return args.Get(0).(*models.TrackStatusResponse), args.Error(1) } -func (m *MockAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { +func (m *MockAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { args := m.Called(publicKey) - okResponse, ok := args.Get(0).(*api.OKResponse) - errResponse, err := args.Get(1).(*api.ErrResponse) - if ok { - return okResponse, nil - } - if err { - return nil, errResponse - } - return nil, nil + return args.Get(0).(*api.OKResponse), args.Error(1) } From 9f2d57ea0361c1d131b55a9d5080456eb80535d1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 10:10:14 +0300 Subject: [PATCH 127/175] update tests --- internal/handlers/ussd/menuhandler_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 3b4b00f..8828690 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -75,11 +75,9 @@ func TestCreateAccount(t *testing.T) { } // Create required mocks flag_account_created, err := fm.GetFlag("flag_account_created") - flag_api_call_error, _ := fm.GetFlag("flag_api_call_error") if err != nil { t.Logf(err.Error()) } - // Define session ID and mock data sessionId := "session123" notFoundErr := db.ErrNotFound{} @@ -101,8 +99,7 @@ func TestCreateAccount(t *testing.T) { }, }, expectedResult: resource.Result{ - FlagSet: []uint32{flag_account_created}, - FlagReset: []uint32{flag_api_call_error}, + FlagSet: []uint32{flag_account_created}, }, }, } From f3a028f1fc73ef2a84b80787585386af59b83920 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 12:03:32 +0300 Subject: [PATCH 128/175] move to testutil --- internal/mocks/dbmock.go | 59 ------------------- internal/mocks/httpmocks/enginemock.go | 30 ---------- .../mocks/httpmocks/requesthandlermock.go | 47 --------------- internal/mocks/httpmocks/requestparsermock.go | 15 ----- internal/mocks/httpmocks/writermock.go | 25 -------- internal/mocks/servicemock.go | 26 -------- internal/mocks/userdbmock.go | 24 -------- 7 files changed, 226 deletions(-) delete mode 100644 internal/mocks/dbmock.go delete mode 100644 internal/mocks/httpmocks/enginemock.go delete mode 100644 internal/mocks/httpmocks/requesthandlermock.go delete mode 100644 internal/mocks/httpmocks/requestparsermock.go delete mode 100644 internal/mocks/httpmocks/writermock.go delete mode 100644 internal/mocks/servicemock.go delete mode 100644 internal/mocks/userdbmock.go diff --git a/internal/mocks/dbmock.go b/internal/mocks/dbmock.go deleted file mode 100644 index 0b40eab..0000000 --- a/internal/mocks/dbmock.go +++ /dev/null @@ -1,59 +0,0 @@ -package mocks - -import ( - "context" - - "git.defalsify.org/vise.git/lang" - "github.com/stretchr/testify/mock" -) - -type MockDb struct { - mock.Mock -} - -func (m *MockDb) SetPrefix(prefix uint8) { - m.Called(prefix) -} - -func (m *MockDb) Prefix() uint8 { - args := m.Called() - return args.Get(0).(uint8) -} - -func (m *MockDb) Safe() bool { - args := m.Called() - return args.Get(0).(bool) -} - -func (m *MockDb) SetLanguage(language *lang.Language) { - m.Called(language) -} - -func (m *MockDb) SetLock(uint8, bool) error { - args := m.Called() - return args.Error(0) -} - -func (m *MockDb) Connect(ctx context.Context, connectionStr string) error { - args := m.Called(ctx, connectionStr) - return args.Error(0) -} - -func (m *MockDb) SetSession(sessionId string) { - m.Called(sessionId) -} - -func (m *MockDb) Put(ctx context.Context, key, value []byte) error { - args := m.Called(ctx, key, value) - return args.Error(0) -} - -func (m *MockDb) Get(ctx context.Context, key []byte) ([]byte, error) { - args := m.Called(ctx, key) - return nil, args.Error(0) -} - -func (m *MockDb) Close() error { - args := m.Called(nil) - return args.Error(0) -} diff --git a/internal/mocks/httpmocks/enginemock.go b/internal/mocks/httpmocks/enginemock.go deleted file mode 100644 index 12d07f4..0000000 --- a/internal/mocks/httpmocks/enginemock.go +++ /dev/null @@ -1,30 +0,0 @@ -package httpmocks - -import ( - "context" - "io" -) - -// MockEngine implements the engine.Engine interface for testing -type MockEngine struct { - InitFunc func(context.Context) (bool, error) - ExecFunc func(context.Context, []byte) (bool, error) - FlushFunc func(context.Context, io.Writer) (int, error) - FinishFunc func() error -} - -func (m *MockEngine) Init(ctx context.Context) (bool, error) { - return m.InitFunc(ctx) -} - -func (m *MockEngine) Exec(ctx context.Context, input []byte) (bool, error) { - return m.ExecFunc(ctx, input) -} - -func (m *MockEngine) Flush(ctx context.Context, w io.Writer) (int, error) { - return m.FlushFunc(ctx, w) -} - -func (m *MockEngine) Finish() error { - return m.FinishFunc() -} diff --git a/internal/mocks/httpmocks/requesthandlermock.go b/internal/mocks/httpmocks/requesthandlermock.go deleted file mode 100644 index f17abce..0000000 --- a/internal/mocks/httpmocks/requesthandlermock.go +++ /dev/null @@ -1,47 +0,0 @@ -package httpmocks - -import ( - "git.defalsify.org/vise.git/engine" - "git.defalsify.org/vise.git/persist" - "git.defalsify.org/vise.git/resource" - "git.grassecon.net/urdt/ussd/internal/handlers" -) - -// MockRequestHandler implements handlers.RequestHandler interface for testing -type MockRequestHandler struct { - ProcessFunc func(handlers.RequestSession) (handlers.RequestSession, error) - GetConfigFunc func() engine.Config - GetEngineFunc func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine - OutputFunc func(rs handlers.RequestSession) (handlers.RequestSession, error) - ResetFunc func(rs handlers.RequestSession) (handlers.RequestSession, error) - ShutdownFunc func() - GetRequestParserFunc func() handlers.RequestParser -} - -func (m *MockRequestHandler) Process(rqs handlers.RequestSession) (handlers.RequestSession, error) { - return m.ProcessFunc(rqs) -} - -func (m *MockRequestHandler) GetConfig() engine.Config { - return m.GetConfigFunc() -} - -func (m *MockRequestHandler) GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine { - return m.GetEngineFunc(cfg, rs, pe) -} - -func (m *MockRequestHandler) Output(rs handlers.RequestSession) (handlers.RequestSession, error) { - return m.OutputFunc(rs) -} - -func (m *MockRequestHandler) Reset(rs handlers.RequestSession) (handlers.RequestSession, error) { - return m.ResetFunc(rs) -} - -func (m *MockRequestHandler) Shutdown() { - m.ShutdownFunc() -} - -func (m *MockRequestHandler) GetRequestParser() handlers.RequestParser { - return m.GetRequestParserFunc() -} diff --git a/internal/mocks/httpmocks/requestparsermock.go b/internal/mocks/httpmocks/requestparsermock.go deleted file mode 100644 index 54b16bf..0000000 --- a/internal/mocks/httpmocks/requestparsermock.go +++ /dev/null @@ -1,15 +0,0 @@ -package httpmocks - -// MockRequestParser implements the handlers.RequestParser interface for testing -type MockRequestParser struct { - GetSessionIdFunc func(any) (string, error) - GetInputFunc func(any) ([]byte, error) -} - -func (m *MockRequestParser) GetSessionId(rq any) (string, error) { - return m.GetSessionIdFunc(rq) -} - -func (m *MockRequestParser) GetInput(rq any) ([]byte, error) { - return m.GetInputFunc(rq) -} diff --git a/internal/mocks/httpmocks/writermock.go b/internal/mocks/httpmocks/writermock.go deleted file mode 100644 index 0d171d2..0000000 --- a/internal/mocks/httpmocks/writermock.go +++ /dev/null @@ -1,25 +0,0 @@ -package httpmocks - -import "net/http" - -// MockWriter implements a mock io.Writer for testing -type MockWriter struct { - WriteStringCalled bool - WrittenString string -} - -func (m *MockWriter) Write(p []byte) (n int, err error) { - return len(p), nil -} - -func (m *MockWriter) WriteString(s string) (n int, err error) { - m.WriteStringCalled = true - m.WrittenString = s - return len(s), nil -} - -func (m *MockWriter) Header() http.Header { - return http.Header{} -} - -func (m *MockWriter) WriteHeader(statusCode int) {} \ No newline at end of file diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go deleted file mode 100644 index d828045..0000000 --- a/internal/mocks/servicemock.go +++ /dev/null @@ -1,26 +0,0 @@ -package mocks - -import ( - "git.grassecon.net/urdt/ussd/internal/models" - "github.com/stretchr/testify/mock" -) - -// MockAccountService implements AccountServiceInterface for testing -type MockAccountService struct { - mock.Mock -} - -func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { - args := m.Called() - return args.Get(0).(*models.AccountResponse), args.Error(1) -} - -func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { - args := m.Called(publicKey) - return args.Get(0).(*models.BalanceResponse), args.Error(1) -} - -func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { - args := m.Called(trackingId) - return args.Get(0).(*models.TrackStatusResponse), args.Error(1) -} \ No newline at end of file diff --git a/internal/mocks/userdbmock.go b/internal/mocks/userdbmock.go deleted file mode 100644 index ff3f18d..0000000 --- a/internal/mocks/userdbmock.go +++ /dev/null @@ -1,24 +0,0 @@ -package mocks - -import ( - "context" - - "git.defalsify.org/vise.git/db" - "git.grassecon.net/urdt/ussd/internal/utils" - "github.com/stretchr/testify/mock" -) - -type MockUserDataStore struct { - db.Db - mock.Mock -} - -func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) { - args := m.Called(ctx, sessionId, typ) - return args.Get(0).([]byte), args.Error(1) -} - -func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ utils.DataTyp, value []byte) error { - args := m.Called(ctx, sessionId, typ, value) - return args.Error(0) -} From 651969668f64e82fcfe18a26b9a18f5b44fef677 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 12:05:11 +0300 Subject: [PATCH 129/175] move to testutil --- internal/testutil/mocks/dbmock.go | 59 +++++++++++++++++++ .../testutil/mocks/httpmocks/enginemock.go | 30 ++++++++++ .../mocks/httpmocks/requesthandlermock.go | 47 +++++++++++++++ .../mocks/httpmocks/requestparsermock.go | 15 +++++ .../testutil/mocks/httpmocks/writermock.go | 25 ++++++++ internal/testutil/mocks/servicemock.go | 26 ++++++++ 6 files changed, 202 insertions(+) create mode 100644 internal/testutil/mocks/dbmock.go create mode 100644 internal/testutil/mocks/httpmocks/enginemock.go create mode 100644 internal/testutil/mocks/httpmocks/requesthandlermock.go create mode 100644 internal/testutil/mocks/httpmocks/requestparsermock.go create mode 100644 internal/testutil/mocks/httpmocks/writermock.go create mode 100644 internal/testutil/mocks/servicemock.go diff --git a/internal/testutil/mocks/dbmock.go b/internal/testutil/mocks/dbmock.go new file mode 100644 index 0000000..0b40eab --- /dev/null +++ b/internal/testutil/mocks/dbmock.go @@ -0,0 +1,59 @@ +package mocks + +import ( + "context" + + "git.defalsify.org/vise.git/lang" + "github.com/stretchr/testify/mock" +) + +type MockDb struct { + mock.Mock +} + +func (m *MockDb) SetPrefix(prefix uint8) { + m.Called(prefix) +} + +func (m *MockDb) Prefix() uint8 { + args := m.Called() + return args.Get(0).(uint8) +} + +func (m *MockDb) Safe() bool { + args := m.Called() + return args.Get(0).(bool) +} + +func (m *MockDb) SetLanguage(language *lang.Language) { + m.Called(language) +} + +func (m *MockDb) SetLock(uint8, bool) error { + args := m.Called() + return args.Error(0) +} + +func (m *MockDb) Connect(ctx context.Context, connectionStr string) error { + args := m.Called(ctx, connectionStr) + return args.Error(0) +} + +func (m *MockDb) SetSession(sessionId string) { + m.Called(sessionId) +} + +func (m *MockDb) Put(ctx context.Context, key, value []byte) error { + args := m.Called(ctx, key, value) + return args.Error(0) +} + +func (m *MockDb) Get(ctx context.Context, key []byte) ([]byte, error) { + args := m.Called(ctx, key) + return nil, args.Error(0) +} + +func (m *MockDb) Close() error { + args := m.Called(nil) + return args.Error(0) +} diff --git a/internal/testutil/mocks/httpmocks/enginemock.go b/internal/testutil/mocks/httpmocks/enginemock.go new file mode 100644 index 0000000..12d07f4 --- /dev/null +++ b/internal/testutil/mocks/httpmocks/enginemock.go @@ -0,0 +1,30 @@ +package httpmocks + +import ( + "context" + "io" +) + +// MockEngine implements the engine.Engine interface for testing +type MockEngine struct { + InitFunc func(context.Context) (bool, error) + ExecFunc func(context.Context, []byte) (bool, error) + FlushFunc func(context.Context, io.Writer) (int, error) + FinishFunc func() error +} + +func (m *MockEngine) Init(ctx context.Context) (bool, error) { + return m.InitFunc(ctx) +} + +func (m *MockEngine) Exec(ctx context.Context, input []byte) (bool, error) { + return m.ExecFunc(ctx, input) +} + +func (m *MockEngine) Flush(ctx context.Context, w io.Writer) (int, error) { + return m.FlushFunc(ctx, w) +} + +func (m *MockEngine) Finish() error { + return m.FinishFunc() +} diff --git a/internal/testutil/mocks/httpmocks/requesthandlermock.go b/internal/testutil/mocks/httpmocks/requesthandlermock.go new file mode 100644 index 0000000..f17abce --- /dev/null +++ b/internal/testutil/mocks/httpmocks/requesthandlermock.go @@ -0,0 +1,47 @@ +package httpmocks + +import ( + "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/persist" + "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/internal/handlers" +) + +// MockRequestHandler implements handlers.RequestHandler interface for testing +type MockRequestHandler struct { + ProcessFunc func(handlers.RequestSession) (handlers.RequestSession, error) + GetConfigFunc func() engine.Config + GetEngineFunc func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine + OutputFunc func(rs handlers.RequestSession) (handlers.RequestSession, error) + ResetFunc func(rs handlers.RequestSession) (handlers.RequestSession, error) + ShutdownFunc func() + GetRequestParserFunc func() handlers.RequestParser +} + +func (m *MockRequestHandler) Process(rqs handlers.RequestSession) (handlers.RequestSession, error) { + return m.ProcessFunc(rqs) +} + +func (m *MockRequestHandler) GetConfig() engine.Config { + return m.GetConfigFunc() +} + +func (m *MockRequestHandler) GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine { + return m.GetEngineFunc(cfg, rs, pe) +} + +func (m *MockRequestHandler) Output(rs handlers.RequestSession) (handlers.RequestSession, error) { + return m.OutputFunc(rs) +} + +func (m *MockRequestHandler) Reset(rs handlers.RequestSession) (handlers.RequestSession, error) { + return m.ResetFunc(rs) +} + +func (m *MockRequestHandler) Shutdown() { + m.ShutdownFunc() +} + +func (m *MockRequestHandler) GetRequestParser() handlers.RequestParser { + return m.GetRequestParserFunc() +} diff --git a/internal/testutil/mocks/httpmocks/requestparsermock.go b/internal/testutil/mocks/httpmocks/requestparsermock.go new file mode 100644 index 0000000..54b16bf --- /dev/null +++ b/internal/testutil/mocks/httpmocks/requestparsermock.go @@ -0,0 +1,15 @@ +package httpmocks + +// MockRequestParser implements the handlers.RequestParser interface for testing +type MockRequestParser struct { + GetSessionIdFunc func(any) (string, error) + GetInputFunc func(any) ([]byte, error) +} + +func (m *MockRequestParser) GetSessionId(rq any) (string, error) { + return m.GetSessionIdFunc(rq) +} + +func (m *MockRequestParser) GetInput(rq any) ([]byte, error) { + return m.GetInputFunc(rq) +} diff --git a/internal/testutil/mocks/httpmocks/writermock.go b/internal/testutil/mocks/httpmocks/writermock.go new file mode 100644 index 0000000..0d171d2 --- /dev/null +++ b/internal/testutil/mocks/httpmocks/writermock.go @@ -0,0 +1,25 @@ +package httpmocks + +import "net/http" + +// MockWriter implements a mock io.Writer for testing +type MockWriter struct { + WriteStringCalled bool + WrittenString string +} + +func (m *MockWriter) Write(p []byte) (n int, err error) { + return len(p), nil +} + +func (m *MockWriter) WriteString(s string) (n int, err error) { + m.WriteStringCalled = true + m.WrittenString = s + return len(s), nil +} + +func (m *MockWriter) Header() http.Header { + return http.Header{} +} + +func (m *MockWriter) WriteHeader(statusCode int) {} \ No newline at end of file diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go new file mode 100644 index 0000000..d828045 --- /dev/null +++ b/internal/testutil/mocks/servicemock.go @@ -0,0 +1,26 @@ +package mocks + +import ( + "git.grassecon.net/urdt/ussd/internal/models" + "github.com/stretchr/testify/mock" +) + +// MockAccountService implements AccountServiceInterface for testing +type MockAccountService struct { + mock.Mock +} + +func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { + args := m.Called() + return args.Get(0).(*models.AccountResponse), args.Error(1) +} + +func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { + args := m.Called(publicKey) + return args.Get(0).(*models.BalanceResponse), args.Error(1) +} + +func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { + args := m.Called(trackingId) + return args.Get(0).(*models.TrackStatusResponse), args.Error(1) +} \ No newline at end of file From 5692440099109d321a7a8487407b82fcd6cb561c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 12:05:27 +0300 Subject: [PATCH 130/175] move to testutil --- internal/testutil/mocks/userdbmock.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 internal/testutil/mocks/userdbmock.go diff --git a/internal/testutil/mocks/userdbmock.go b/internal/testutil/mocks/userdbmock.go new file mode 100644 index 0000000..ff3f18d --- /dev/null +++ b/internal/testutil/mocks/userdbmock.go @@ -0,0 +1,24 @@ +package mocks + +import ( + "context" + + "git.defalsify.org/vise.git/db" + "git.grassecon.net/urdt/ussd/internal/utils" + "github.com/stretchr/testify/mock" +) + +type MockUserDataStore struct { + db.Db + mock.Mock +} + +func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) { + args := m.Called(ctx, sessionId, typ) + return args.Get(0).([]byte), args.Error(1) +} + +func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ utils.DataTyp, value []byte) error { + args := m.Called(ctx, sessionId, typ, value) + return args.Error(0) +} From 57a07af8caee42c2a5a64abffaa41e7203a93fa5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 12:06:44 +0300 Subject: [PATCH 131/175] correct import --- internal/http/http_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/http_test.go b/internal/http/http_test.go index 8f6f312..14bb90a 100644 --- a/internal/http/http_test.go +++ b/internal/http/http_test.go @@ -13,7 +13,7 @@ import ( "git.defalsify.org/vise.git/engine" "git.grassecon.net/urdt/ussd/internal/handlers" - "git.grassecon.net/urdt/ussd/internal/mocks/httpmocks" + "git.grassecon.net/urdt/ussd/internal/testutil/mocks/httpmocks" ) // invalidRequestType is a custom type to test invalid request scenarios From f99486c19067bfe084e08b1811500c6173b8e350 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 12:07:00 +0300 Subject: [PATCH 132/175] correct import --- internal/handlers/ussd/menuhandler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 42e97fd..482e997 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -15,8 +15,8 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" - "git.grassecon.net/urdt/ussd/internal/mocks" "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "git.grassecon.net/urdt/ussd/internal/utils" From 579b46db65b858b25f5196215f11ed08eeb120d5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 14:34:12 +0300 Subject: [PATCH 133/175] Replace the public key with the sessionId --- internal/handlers/ussd/menuhandler.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 36d1ad5..bdb9fb7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -906,7 +906,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, nil } -// GetSender retrieves the public key from the Gdbm Db +// GetSender returns the sessionId (phoneNumber) func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -915,10 +915,7 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res return res, fmt.Errorf("missing session") } - store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - - res.Content = string(publicKey) + res.Content = string(sessionId) return res, nil } @@ -955,13 +952,12 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] // TODO // Use the amount, recipient and sender to call the API and initialize the transaction store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) - res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey)) + res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(sessionId)) account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") if err != nil { From 6200728435c09aa010f65a66862fbcd2e6420bb8 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 14:45:15 +0300 Subject: [PATCH 134/175] Updated the menuhander test --- internal/handlers/ussd/menuhandler_test.go | 27 +++++++--------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 38c468c..bd72d65 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -509,12 +509,8 @@ func TestGetSender(t *testing.T) { mockStore := new(mocks.MockUserDataStore) // Define test data - sessionId := "session123" + sessionId := "254712345678" ctx := context.WithValue(context.Background(), "SessionId", sessionId) - publicKey := "0xcasgatweksalw1018221" - - // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -522,11 +518,10 @@ func TestGetSender(t *testing.T) { } // Call the method - res, _ := h.GetSender(ctx, "max_amount", []byte("check_balance")) - - //Assert that the public key from readentry operation is what was set as the result content. - assert.Equal(t, publicKey, res.Content) + res, _ := h.GetSender(ctx, "get_sender", []byte("")) + //Assert that the sessionId is what was set as the result content. + assert.Equal(t, sessionId, res.Content) } func TestGetAmount(t *testing.T) { @@ -1284,7 +1279,7 @@ func TestResetInvalidAmount(t *testing.T) { } func TestInitiateTransaction(t *testing.T) { - sessionId := "session123" + sessionId := "254712345678" fm, err := NewFlagManager(flagsPath) @@ -1307,30 +1302,26 @@ func TestInitiateTransaction(t *testing.T) { tests := []struct { name string input []byte - PublicKey []byte Recipient []byte Amount []byte status string expectedResult resource.Result }{ { - name: "Test amount reset", - PublicKey: []byte("0x1241527192"), - Amount: []byte("0.002CELO"), + name: "Test initiate transaction", + Amount: []byte("0.002 CELO"), Recipient: []byte("0x12415ass27192"), expectedResult: resource.Result{ FlagReset: []uint32{account_authorized_flag}, - Content: "Your request has been sent. 0x12415ass27192 will receive 0.002CELO from 0x1241527192.", + Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 CELO from 254712345678.", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.PublicKey, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) - //mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1343,10 +1334,8 @@ func TestInitiateTransaction(t *testing.T) { // Assert that expectations were met mockDataStore.AssertExpectations(t) - }) } - } func TestQuit(t *testing.T) { From 75459f852b8b5b8df43a2ccb1cf05db1e159c877 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 15:12:57 +0300 Subject: [PATCH 135/175] Return the full balance string --- internal/handlers/ussd/menuhandler.go | 7 ++++++- services/registration/locale/swa/default.po | 4 +++- services/registration/main | 2 +- services/registration/main_swa | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 36d1ad5..ce56a58 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -650,6 +650,10 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, fmt.Errorf("missing session") } + code := codeFromCtx(ctx) + l := gotext.NewLocale(translationDir, code) + l.AddDomain("default") + store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { @@ -666,7 +670,8 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( } res.FlagReset = append(res.FlagReset, flag_api_error) balance := balanceResponse.Result.Balance - res.Content = balance + + res.Content = l.Get("Balance: %s\n", balance) return res, nil } diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index 5289dd7..0a3909b 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -7,6 +7,8 @@ msgstr "Ombi lako limetumwa. %s atapokea %s kutoka kwa %s." msgid "Thank you for using Sarafu. Goodbye!" msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!" - msgid "For more help,please call: 0757628885" msgstr "Kwa usaidizi zaidi,piga: 0757628885" + +msgid "Balance: %s\n" +msgstr "Salio: %s\n" diff --git a/services/registration/main b/services/registration/main index bf15ea5..afae8c1 100644 --- a/services/registration/main +++ b/services/registration/main @@ -1 +1 @@ -Balance: {{.check_balance}} +{{.check_balance}} \ No newline at end of file diff --git a/services/registration/main_swa b/services/registration/main_swa index b72abf0..afae8c1 100644 --- a/services/registration/main_swa +++ b/services/registration/main_swa @@ -1 +1 @@ -Salio: {{.check_balance}} +{{.check_balance}} \ No newline at end of file From 39c0560abef514ca1fa49083444f8874146e6c4f Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 15:21:48 +0300 Subject: [PATCH 136/175] Updated the test --- internal/handlers/ussd/menuhandler_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 38c468c..672a4fa 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1625,7 +1625,6 @@ func TestValidateRecipient(t *testing.T) { } func TestCheckBalance(t *testing.T) { - sessionId := "session123" publicKey := "0X13242618721" fm, _ := NewFlagManager(flagsPath) @@ -1655,7 +1654,7 @@ func TestCheckBalance(t *testing.T) { }, }, { - name: "Test when checking a balance is a success", + name: "Test when checking a balance is a success", balanceResonse: &models.BalanceResponse{ Ok: true, Result: struct { @@ -1667,7 +1666,7 @@ func TestCheckBalance(t *testing.T) { }, }, expectedResult: resource.Result{ - Content: "0.003 CELO", + Content: "Balance: 0.003 CELO\n", FlagReset: []uint32{flag_api_error}, }, }, @@ -1700,10 +1699,8 @@ func TestCheckBalance(t *testing.T) { //Assert that the result set to content is what was expected assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") - }) } - } func TestGetProfile(t *testing.T) { From abc01b7cee44a3bf39f2874b9a0f787b663c8de1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 16:21:34 +0300 Subject: [PATCH 137/175] change to local setup endpoint --- config/config.go | 2 +- internal/handlers/server/accountservice.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/config.go b/config/config.go index 43466c3..0ba88a4 100644 --- a/config/config.go +++ b/config/config.go @@ -11,7 +11,7 @@ var ( // LoadConfig initializes the configuration values after environment variables are loaded. func LoadConfig() { - CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create") + CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/creates") TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status") diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 6ae9630..9534520 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -14,9 +14,8 @@ import ( ) var ( - okResponse api.OKResponse - errResponse api.ErrResponse - EMPTY_RESPONSE = 0 + okResponse api.OKResponse + errResponse api.ErrResponse ) type AccountServiceInterface interface { @@ -93,7 +92,7 @@ func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, } return nil, errors.New(errResponse.Description) } - if len(okResponse.Result) == EMPTY_RESPONSE { + if len(okResponse.Result) == 0 { return nil, errors.New("Empty api result") } return &okResponse, nil @@ -150,6 +149,7 @@ func (as *AccountService) CreateAccount() (*api.OKResponse, error) { return nil, err } err = json.Unmarshal([]byte(body), &okResponse) + if err != nil { err := json.Unmarshal([]byte(body), &errResponse) if err != nil { @@ -157,7 +157,7 @@ func (as *AccountService) CreateAccount() (*api.OKResponse, error) { } return nil, errors.New(errResponse.Description) } - if len(okResponse.Result) == EMPTY_RESPONSE { + if len(okResponse.Result) == 0 { return nil, errors.New("Empty api result") } return &okResponse, nil From ee9a683eb0f6824e9e342d1f7dd85714a597200d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 16:30:44 +0300 Subject: [PATCH 138/175] Merge branch 'master' into menu-voucherlist --- internal/handlers/ussd/menuhandler.go | 10 +++----- internal/handlers/ussd/menuhandler_test.go | 27 +++++++--------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f178231..a9cb5b7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -849,7 +849,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, nil } -// GetSender retrieves the public key from the Gdbm Db +// GetSender returns the sessionId (phoneNumber) func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -858,10 +858,7 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res return res, fmt.Errorf("missing session") } - store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - - res.Content = string(publicKey) + res.Content = string(sessionId) return res, nil } @@ -898,13 +895,12 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] // TODO // Use the amount, recipient and sender to call the API and initialize the transaction store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) - res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey)) + res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(sessionId)) account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") if err != nil { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index e201531..e86167b 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -478,12 +478,8 @@ func TestGetSender(t *testing.T) { mockStore := new(mocks.MockUserDataStore) // Define test data - sessionId := "session123" + sessionId := "254712345678" ctx := context.WithValue(context.Background(), "SessionId", sessionId) - publicKey := "0xcasgatweksalw1018221" - - // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -491,11 +487,10 @@ func TestGetSender(t *testing.T) { } // Call the method - res, _ := h.GetSender(ctx, "max_amount", []byte("check_balance")) - - //Assert that the public key from readentry operation is what was set as the result content. - assert.Equal(t, publicKey, res.Content) + res, _ := h.GetSender(ctx, "get_sender", []byte("")) + //Assert that the sessionId is what was set as the result content. + assert.Equal(t, sessionId, res.Content) } func TestGetAmount(t *testing.T) { @@ -1256,7 +1251,7 @@ func TestResetInvalidAmount(t *testing.T) { } func TestInitiateTransaction(t *testing.T) { - sessionId := "session123" + sessionId := "254712345678" fm, err := NewFlagManager(flagsPath) @@ -1279,30 +1274,26 @@ func TestInitiateTransaction(t *testing.T) { tests := []struct { name string input []byte - PublicKey []byte Recipient []byte Amount []byte status string expectedResult resource.Result }{ { - name: "Test amount reset", - PublicKey: []byte("0x1241527192"), - Amount: []byte("0.002CELO"), + name: "Test initiate transaction", + Amount: []byte("0.002 CELO"), Recipient: []byte("0x12415ass27192"), expectedResult: resource.Result{ FlagReset: []uint32{account_authorized_flag}, - Content: "Your request has been sent. 0x12415ass27192 will receive 0.002CELO from 0x1241527192.", + Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 CELO from 254712345678.", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.PublicKey, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) - //mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1315,10 +1306,8 @@ func TestInitiateTransaction(t *testing.T) { // Assert that expectations were met mockDataStore.AssertExpectations(t) - }) } - } func TestQuit(t *testing.T) { From 2b34a0900c3640a994a3f75b2927382018b8c932 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 16:35:53 +0300 Subject: [PATCH 139/175] check for status code and unmarshal on errResponse --- internal/handlers/server/accountservice.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 9534520..27896e7 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -84,14 +84,17 @@ func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, errResponse.Description = err.Error() return nil, err } - err = json.Unmarshal([]byte(body), &okResponse) - if err != nil { + if resp.StatusCode >= http.StatusBadRequest { err := json.Unmarshal([]byte(body), &errResponse) if err != nil { return nil, err } return nil, errors.New(errResponse.Description) } + err = json.Unmarshal([]byte(body), &okResponse) + if err != nil { + return nil, err + } if len(okResponse.Result) == 0 { return nil, errors.New("Empty api result") } @@ -148,15 +151,17 @@ func (as *AccountService) CreateAccount() (*api.OKResponse, error) { if err != nil { return nil, err } - err = json.Unmarshal([]byte(body), &okResponse) - - if err != nil { + if resp.StatusCode >= http.StatusBadRequest { err := json.Unmarshal([]byte(body), &errResponse) if err != nil { return nil, err } return nil, errors.New(errResponse.Description) } + err = json.Unmarshal([]byte(body), &okResponse) + if err != nil { + return nil, err + } if len(okResponse.Result) == 0 { return nil, errors.New("Empty api result") } From c796bbdcfcc535cd05fc14d552c0d02dea27a996 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 16:36:09 +0300 Subject: [PATCH 140/175] correct create endpoint --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 0ba88a4..43466c3 100644 --- a/config/config.go +++ b/config/config.go @@ -11,7 +11,7 @@ var ( // LoadConfig initializes the configuration values after environment variables are loaded. func LoadConfig() { - CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/creates") + CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create") TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status") From 69a4530269f35d428aff1cd66ea41464ba7b257e Mon Sep 17 00:00:00 2001 From: carlos Date: Thu, 24 Oct 2024 15:41:47 +0200 Subject: [PATCH 141/175] Delete services/registration/locale/swa/default.mo remove dev file --- services/registration/locale/swa/default.mo | Bin 67 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 services/registration/locale/swa/default.mo diff --git a/services/registration/locale/swa/default.mo b/services/registration/locale/swa/default.mo deleted file mode 100644 index e0ceea9b640e4a5c6a1f3b961292aa843b9d620c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67 ycmca7#4?ou2pEA_28dOFm>Gz5fS47CEr6H>i0yzF20&^(Qgd?h89efH5*YxXiUslj From cff50538fa60feddda030b7535a7681cd89b1044 Mon Sep 17 00:00:00 2001 From: carlos Date: Thu, 24 Oct 2024 15:45:29 +0200 Subject: [PATCH 142/175] Delete cover.out delete cover.out --- cover.out | 738 ------------------------------------------------------ 1 file changed, 738 deletions(-) delete mode 100644 cover.out diff --git a/cover.out b/cover.out deleted file mode 100644 index 8f9f054..0000000 --- a/cover.out +++ /dev/null @@ -1,738 +0,0 @@ -mode: set -git.grassecon.net/urdt/ussd/cmd/main.go:22.13,50.16 18 0 -git.grassecon.net/urdt/ussd/cmd/main.go:50.16,53.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:55.2,56.16 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:56.16,59.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:61.2,62.16 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:62.16,65.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:67.2,68.16 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:68.16,71.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:73.2,74.9 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:74.9,77.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:79.2,83.16 4 0 -git.grassecon.net/urdt/ussd/cmd/main.go:83.16,86.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:88.2,89.16 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:89.16,92.3 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:94.2,96.17 3 0 -git.grassecon.net/urdt/ussd/cmd/main.go:96.17,98.3 1 0 -git.grassecon.net/urdt/ussd/cmd/main.go:100.2,101.16 2 0 -git.grassecon.net/urdt/ussd/cmd/main.go:101.16,104.3 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:30.66,32.2 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:34.62,36.2 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:38.13,66.17 20 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:66.17,68.3 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:70.2,72.16 3 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:72.16,75.3 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:77.2,78.16 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:78.16,81.3 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:83.2,84.16 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:84.16,87.3 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:88.2,91.9 3 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:91.9,93.3 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:95.2,99.16 4 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:99.16,102.3 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:104.2,105.16 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:105.16,108.3 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:109.2,126.12 10 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:126.12,127.10 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:128.19,128.19 0 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:129.20,129.20 0 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:131.3,131.16 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:134.2,134.11 1 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:134.11,136.17 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:136.17,140.4 3 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:141.3,142.17 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:142.17,146.4 3 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:147.3,148.17 2 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:148.17,152.4 3 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:153.3,155.17 3 0 -git.grassecon.net/urdt/ussd/cmd/async/main.go:155.17,159.4 3 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:31.66,33.9 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:33.9,35.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:36.2,36.40 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:36.40,38.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:40.2,41.23 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:41.23,43.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:45.2,45.25 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:48.62,50.9 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:50.9,52.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:53.2,53.40 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:53.40,55.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:57.2,60.21 3 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:60.21,62.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:64.2,64.41 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:67.13,93.17 18 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:93.17,95.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:97.2,99.16 3 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:99.16,102.3 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:104.2,105.16 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:105.16,108.3 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:110.2,111.16 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:111.16,114.3 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:115.2,118.9 3 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:118.9,120.3 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:122.2,125.16 3 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:125.16,128.3 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:130.2,131.16 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:131.16,134.3 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:136.2,137.16 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:137.16,140.3 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:141.2,156.12 11 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:156.12,157.10 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:158.19,158.19 0 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:159.20,159.20 0 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:161.3,161.18 1 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:163.2,164.16 2 0 -git.grassecon.net/urdt/ussd/cmd/africastalking/main.go:164.16,166.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:32.102,34.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:34.16,36.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:37.2,40.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:40.16,42.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:44.2,46.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:46.16,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:52.2,52.24 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:58.91,61.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:61.16,63.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:64.2,67.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:67.16,69.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:71.2,73.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:73.16,75.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:78.2,78.26 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:87.76,89.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:89.16,91.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:92.2,95.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:95.16,97.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:99.2,101.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:101.16,103.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:105.2,105.26 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:21.159,29.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:31.40,33.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:33.16,35.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:38.117,42.2 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:44.81,52.16 6 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:52.16,55.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:57.2,60.9 4 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:60.9,63.18 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:63.18,65.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:66.3,66.28 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:68.2,69.28 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:69.28,71.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:72.2,75.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:75.16,78.18 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:78.18,80.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:81.3,81.18 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:84.2,85.17 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:88.81,92.2 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:94.79,97.2 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:99.55,101.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:103.62,105.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:16.64,19.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:19.16,21.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:22.2,22.24 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:34.156,36.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:36.16,38.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:39.2,44.8 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:47.68,49.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:51.56,53.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:55.69,57.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:57.16,59.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:60.2,98.26 38 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:102.66,106.2 3 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:28.13,54.17 18 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:54.17,56.3 1 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:58.2,60.16 3 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:60.16,63.3 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:65.2,66.16 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:66.16,69.3 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:71.2,72.16 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:72.16,75.3 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:76.2,79.9 3 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:79.9,81.3 1 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:83.2,86.16 3 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:86.16,89.3 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:91.2,92.16 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:92.16,95.3 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:97.2,98.16 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:98.16,101.3 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:102.2,117.12 11 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:117.12,118.10 1 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:119.19,119.19 0 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:120.20,120.20 0 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:122.3,122.18 1 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:124.2,125.16 2 0 -git.grassecon.net/urdt/ussd/cmd/http/main.go:125.16,127.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:32.102,34.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:34.16,36.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:37.2,40.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:40.16,42.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:44.2,46.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:46.16,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:52.2,52.24 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:58.91,61.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:61.16,63.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:64.2,67.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:67.16,69.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:71.2,73.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:73.16,75.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:78.2,78.26 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:87.76,89.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:89.16,91.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:92.2,95.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:95.16,97.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:99.2,101.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:101.16,103.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:105.2,105.26 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:38.59,41.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:41.16,43.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:45.2,47.8 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:51.62,53.2 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:64.84,65.26 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:65.26,67.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:68.2,76.15 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:83.34,86.2 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:88.67,89.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:89.17,90.33 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:92.2,93.10 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:96.97,99.17 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:99.17,102.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:103.2,105.32 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:105.32,108.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:109.2,113.15 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:117.104,123.32 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:123.32,125.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:126.2,130.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:130.16,132.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:133.2,135.17 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:138.108,146.31 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:146.31,149.17 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:149.17,151.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:153.2,155.12 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:162.106,166.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:166.9,168.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:169.2,171.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:171.16,172.25 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:172.25,175.18 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:175.18,177.5 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:180.2,180.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:184.100,189.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:189.9,191.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:193.2,197.29 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:197.29,200.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:202.2,205.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:205.16,207.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:208.2,208.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:211.105,214.9 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:214.9,216.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:217.2,220.26 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:220.26,222.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:222.8,224.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:226.2,226.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:229.109,234.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:234.9,236.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:237.2,242.29 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:242.29,245.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:246.2,248.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:248.16,250.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:251.2,251.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:254.109,257.9 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:257.9,259.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:260.2,264.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:264.16,266.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:267.2,267.38 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:267.38,269.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:269.8,271.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:272.2,273.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:273.16,275.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:276.2,276.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:282.102,290.9 6 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:290.9,292.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:293.2,295.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:295.16,297.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:299.2,299.36 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:299.36,303.3 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:303.8,305.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:307.2,307.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:311.46,313.34 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:313.34,316.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:317.2,317.13 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:321.106,325.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:325.9,327.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:328.2,328.20 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:328.20,332.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:332.17,334.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:337.2,337.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:341.107,345.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:345.9,347.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:349.2,349.20 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:349.20,353.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:353.17,355.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:356.8,358.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:360.2,360.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:364.100,368.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:368.9,370.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:372.2,372.21 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:372.21,376.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:376.17,378.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:381.2,381.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:385.105,389.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:389.9,391.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:393.2,393.20 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:393.20,397.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:397.17,399.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:402.2,402.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:406.103,411.9 5 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:411.9,413.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:415.2,418.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:418.16,420.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:422.2,422.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:426.106,430.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:430.9,432.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:434.2,434.20 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:434.20,438.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:438.17,440.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:443.2,443.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:447.109,454.2 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:457.115,464.2 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:467.108,471.9 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:471.9,473.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:475.2,480.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:485.102,490.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:490.9,492.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:494.2,500.16 6 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:500.16,502.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:503.2,503.21 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:503.21,504.37 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:504.37,505.54 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:505.54,508.5 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:508.10,511.5 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:512.9,516.4 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:517.8,519.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:520.2,520.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:524.110,529.2 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:533.111,541.9 6 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:541.9,543.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:544.2,546.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:546.16,548.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:550.2,551.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:551.16,554.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:555.2,555.23 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:555.23,558.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:559.2,563.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:563.16,565.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:566.2,566.58 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:566.58,569.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:569.8,572.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:573.2,573.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:577.97,589.2 8 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:592.105,604.2 8 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:607.102,615.16 6 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:615.16,619.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:621.2,621.20 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:621.20,623.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:623.8,625.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:627.2,627.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:631.110,638.2 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:642.105,649.9 5 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:649.9,651.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:653.2,655.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:655.16,657.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:659.2,660.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:660.16,662.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:663.2,663.25 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:663.25,666.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:667.2,671.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:674.115,679.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:679.9,681.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:682.2,687.16 5 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:687.16,689.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:691.2,692.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:692.16,694.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:695.2,695.25 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:695.25,698.3 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:699.2,702.21 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:703.12,704.59 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:705.19,706.69 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:707.10,708.8 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:710.2,710.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:714.110,719.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:719.9,721.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:723.2,727.22 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:727.22,729.25 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:729.25,734.4 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:735.3,737.17 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:737.17,739.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:742.2,742.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:747.109,752.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:752.9,754.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:756.2,760.16 5 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:760.16,762.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:764.2,765.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:765.16,767.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:769.2,771.17 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:775.115,780.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:780.9,782.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:784.2,787.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:787.16,789.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:791.2,793.17 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:798.102,803.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:803.9,805.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:806.2,810.16 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:810.16,812.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:813.2,817.17 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:822.107,827.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:827.9,829.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:831.2,841.16 7 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:841.16,843.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:844.2,848.28 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:848.28,850.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:851.2,852.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:852.16,854.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:857.2,859.22 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:859.22,863.3 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:865.2,866.16 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:866.16,870.3 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:872.2,872.32 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:872.32,876.3 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:878.2,880.16 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:880.16,882.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:884.2,884.17 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:888.105,892.9 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:892.9,894.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:895.2,900.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:904.102,908.9 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:908.9,910.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:912.2,917.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:921.102,925.9 3 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:925.9,927.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:928.2,933.17 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:938.108,942.9 4 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:942.9,944.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:946.2,954.16 7 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:954.16,956.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:957.2,958.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:958.16,960.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:961.2,963.17 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:968.112,972.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:972.9,974.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:976.2,991.16 10 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:991.16,993.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:995.2,996.17 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:999.107,1003.9 4 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1003.9,1005.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1006.2,1007.9 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1007.9,1009.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1010.2,1011.19 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1011.19,1013.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1013.8,1015.3 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1018.2,1018.60 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1018.60,1019.33 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1019.33,1021.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1022.3,1022.23 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1024.2,1035.32 9 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1035.32,1036.32 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1036.32,1038.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1038.9,1040.4 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1044.2,1045.25 2 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1045.25,1046.51 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1046.51,1048.4 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1048.9,1050.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1052.2,1052.23 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1053.13,1057.4 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1058.13,1062.4 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1063.10,1067.4 1 1 -git.grassecon.net/urdt/ussd/internal/handlers/ussd/menuhandler.go:1070.2,1070.17 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:14.42,16.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:18.33,21.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:23.30,26.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:28.55,30.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:32.45,35.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:37.75,40.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:42.47,44.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:46.68,49.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:51.71,54.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:56.32,59.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/servicemock.go:13.79,16.2 2 1 -git.grassecon.net/urdt/ussd/internal/mocks/servicemock.go:18.94,21.2 2 1 -git.grassecon.net/urdt/ussd/internal/mocks/servicemock.go:23.105,26.2 2 1 -git.grassecon.net/urdt/ussd/internal/mocks/userdbmock.go:16.113,19.2 2 1 -git.grassecon.net/urdt/ussd/internal/mocks/userdbmock.go:21.118,24.2 2 1 -git.grassecon.net/urdt/ussd/internal/utils/age.go:7.51,13.29 6 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:13.29,15.3 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:16.2,18.30 3 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:18.30,20.3 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:21.2,21.12 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:32.39,35.2 2 1 -git.grassecon.net/urdt/ussd/internal/utils/db.go:28.37,32.2 3 0 -git.grassecon.net/urdt/ussd/internal/utils/db.go:34.47,37.2 2 0 -git.grassecon.net/urdt/ussd/internal/utils/isocode.go:9.38,11.2 1 1 -git.grassecon.net/urdt/ussd/internal/utils/userStore.go:20.107,25.2 4 0 -git.grassecon.net/urdt/ussd/internal/utils/userStore.go:27.112,32.2 4 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:14.42,16.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:18.33,21.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:23.30,26.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:28.55,30.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:32.45,35.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:37.75,40.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:42.47,44.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:46.68,49.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:51.71,54.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/dbmock.go:56.32,59.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/servicemock.go:13.79,16.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/servicemock.go:18.94,21.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/servicemock.go:23.105,26.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/userdbmock.go:16.113,19.2 2 0 -git.grassecon.net/urdt/ussd/internal/mocks/userdbmock.go:21.118,24.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:20.38,21.16 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:21.16,23.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:24.2,24.24 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:27.76,30.8 3 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:30.8,33.3 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:34.2,36.16 3 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:36.16,38.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:39.2,42.12 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:45.35,46.19 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:46.19,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:51.35,52.19 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:52.19,54.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:55.2,56.14 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:59.46,62.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:64.54,67.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:69.57,72.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:74.37,79.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:81.40,86.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:88.63,93.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:95.80,100.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:102.78,107.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:109.39,115.2 5 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:27.86,36.2 3 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:38.73,40.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:42.79,44.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:46.47,48.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:35.82,40.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:42.93,46.16 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:46.16,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:49.2,51.16 3 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:54.81,58.16 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:58.16,60.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:61.2,61.30 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:64.91,67.16 3 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:67.16,69.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:70.2,71.17 2 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:74.81,75.26 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:75.26,76.44 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:78.2,81.16 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:81.16,83.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:84.2,84.27 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:87.51,89.16 2 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:89.16,91.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:92.2,92.12 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:95.45,99.47 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:99.47,101.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:102.2,102.12 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:7.51,13.29 6 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:13.29,15.3 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:16.2,18.30 3 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:18.30,20.3 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:21.2,21.12 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:32.39,35.2 2 0 -git.grassecon.net/urdt/ussd/internal/utils/db.go:28.37,32.2 3 0 -git.grassecon.net/urdt/ussd/internal/utils/db.go:34.47,37.2 2 0 -git.grassecon.net/urdt/ussd/internal/utils/isocode.go:9.38,11.2 1 0 -git.grassecon.net/urdt/ussd/internal/utils/userStore.go:20.107,25.2 4 0 -git.grassecon.net/urdt/ussd/internal/utils/userStore.go:27.112,32.2 4 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:21.159,29.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:31.40,33.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:33.16,35.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:38.117,42.2 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:44.81,52.16 6 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:52.16,55.3 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:57.2,60.9 4 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:60.9,63.18 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:63.18,65.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:66.3,66.28 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:68.2,69.28 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:69.28,71.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:72.2,75.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:75.16,78.18 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:78.18,80.4 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:81.3,81.18 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:84.2,85.17 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:88.81,92.2 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:94.79,97.2 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:99.55,101.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/base.go:103.62,105.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:16.64,19.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:19.16,21.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:22.2,22.24 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:34.156,36.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:36.16,38.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:39.2,44.8 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:47.68,49.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:51.56,53.2 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:55.69,57.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:57.16,59.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:60.2,98.26 38 0 -git.grassecon.net/urdt/ussd/internal/handlers/handlerservice.go:102.66,106.2 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:32.102,34.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:34.16,36.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:37.2,40.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:40.16,42.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:44.2,46.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:46.16,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:52.2,52.24 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:58.91,61.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:61.16,63.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:64.2,67.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:67.16,69.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:71.2,73.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:73.16,75.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:78.2,78.26 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:87.76,89.16 2 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:89.16,91.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:92.2,95.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:95.16,97.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:99.2,101.16 3 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:101.16,103.3 1 0 -git.grassecon.net/urdt/ussd/internal/handlers/server/accountservice.go:105.2,105.26 1 0 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:14.71,18.2 1 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:20.82,32.16 7 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:32.16,36.3 3 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:37.2,39.16 3 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:39.16,43.3 3 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:45.2,46.13 2 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:47.11,48.13 1 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:49.99,50.13 1 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:51.10,52.13 1 0 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:55.2,55.17 1 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:55.17,58.3 2 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:60.2,63.16 4 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:63.16,66.3 2 0 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:68.2,69.16 2 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:69.16,72.3 2 0 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:75.99,79.18 3 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:79.18,81.3 1 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:81.8,83.3 1 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:85.2,86.16 2 1 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:86.16,88.3 1 0 -git.grassecon.net/urdt/ussd/internal/http/at_session_handler.go:90.2,91.17 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:21.69,23.9 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:23.9,25.3 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:26.2,27.13 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:27.13,29.3 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:30.2,30.15 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:33.65,35.9 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:35.9,37.3 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:38.2,40.16 3 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:40.16,42.3 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:43.2,43.15 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:50.66,54.2 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:56.80,61.16 5 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:61.16,64.3 2 0 -git.grassecon.net/urdt/ussd/internal/http/server.go:65.2,65.8 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:68.77,81.16 8 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:81.16,84.3 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:85.2,87.16 3 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:87.16,91.3 3 0 -git.grassecon.net/urdt/ussd/internal/http/server.go:93.2,94.13 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:95.27,96.13 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:97.30,98.13 1 0 -git.grassecon.net/urdt/ussd/internal/http/server.go:99.30,100.13 1 0 -git.grassecon.net/urdt/ussd/internal/http/server.go:101.10,102.13 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:105.2,105.17 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:105.17,108.3 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:110.2,114.16 5 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:114.16,117.3 2 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:118.2,118.17 1 1 -git.grassecon.net/urdt/ussd/internal/http/server.go:118.17,121.3 2 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:16.62,18.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:20.76,22.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:24.75,26.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:28.37,30.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:21.100,23.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:25.56,27.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:29.118,31.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:33.98,35.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:37.97,39.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:41.41,43.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:45.72,47.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requestparsermock.go:9.66,11.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requestparsermock.go:13.62,15.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:11.57,13.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:15.63,19.2 3 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:21.43,23.2 1 1 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:25.51,25.52 0 1 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:20.38,21.16 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:21.16,23.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:24.2,24.24 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:27.76,30.8 3 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:30.8,33.3 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:34.2,36.16 3 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:36.16,38.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:39.2,42.12 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:45.35,46.19 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:46.19,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:51.35,52.19 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:52.19,54.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:55.2,56.14 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:59.46,62.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:64.54,67.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:69.57,72.2 2 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:74.37,79.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:81.40,86.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:88.63,93.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:95.80,100.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:102.78,107.2 4 0 -git.grassecon.net/urdt/ussd/internal/storage/gdbm.go:109.39,115.2 5 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:27.86,36.2 3 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:38.73,40.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:42.79,44.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storage.go:46.47,48.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:35.82,40.2 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:42.93,46.16 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:46.16,48.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:49.2,51.16 3 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:54.81,58.16 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:58.16,60.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:61.2,61.30 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:64.91,67.16 3 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:67.16,69.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:70.2,71.17 2 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:74.81,75.26 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:75.26,76.44 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:78.2,81.16 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:81.16,83.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:84.2,84.27 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:87.51,89.16 2 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:89.16,91.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:92.2,92.12 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:95.45,99.47 4 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:99.47,101.3 1 0 -git.grassecon.net/urdt/ussd/internal/storage/storageservice.go:102.2,102.12 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:7.51,13.29 6 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:13.29,15.3 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:16.2,18.30 3 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:18.30,20.3 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:21.2,21.12 1 0 -git.grassecon.net/urdt/ussd/internal/utils/age.go:32.39,35.2 2 0 -git.grassecon.net/urdt/ussd/internal/utils/db.go:28.37,32.2 3 0 -git.grassecon.net/urdt/ussd/internal/utils/db.go:34.47,37.2 2 0 -git.grassecon.net/urdt/ussd/internal/utils/isocode.go:9.38,11.2 1 0 -git.grassecon.net/urdt/ussd/internal/utils/userStore.go:20.107,25.2 4 0 -git.grassecon.net/urdt/ussd/internal/utils/userStore.go:27.112,32.2 4 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:16.62,18.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:20.76,22.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:24.75,26.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/enginemock.go:28.37,30.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:21.100,23.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:25.56,27.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:29.118,31.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:33.98,35.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:37.97,39.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:41.41,43.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requesthandlermock.go:45.72,47.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requestparsermock.go:9.66,11.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/requestparsermock.go:13.62,15.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:11.57,13.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:15.63,19.2 3 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:21.43,23.2 1 0 -git.grassecon.net/urdt/ussd/internal/mocks/httpmocks/writermock.go:25.51,25.52 0 0 From 5c75e35fe0e8ff81f251df52965af5063090addc Mon Sep 17 00:00:00 2001 From: carlos Date: Thu, 24 Oct 2024 15:45:52 +0200 Subject: [PATCH 143/175] Delete coverage.html delete cover.html --- coverage.html | 2961 ------------------------------------------------- 1 file changed, 2961 deletions(-) delete mode 100644 coverage.html diff --git a/coverage.html b/coverage.html deleted file mode 100644 index a448bc0..0000000 --- a/coverage.html +++ /dev/null @@ -1,2961 +0,0 @@ - - - - - - africastalking: Go Coverage Report - - - -
- -
- not tracked - - not covered - covered - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - From 383f074cae99a2a72f17a926f3bb3b823055b469 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 17:32:08 +0300 Subject: [PATCH 144/175] update method signatures --- internal/handlers/server/accountservice.go | 28 +++++++--------------- internal/handlers/ussd/menuhandler.go | 10 ++++---- internal/mocks/servicemock.go | 6 ++--- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index d449f2f..1b5272a 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -20,10 +20,10 @@ var ( ) type AccountServiceInterface interface { - CheckBalance(publicKey string, ctx context.Context) (*models.BalanceResponse, error) + CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) CreateAccount(ctx context.Context) (*api.OKResponse, error) - CheckAccountStatus(trackingId string, ctx context.Context) (*models.TrackStatusResponse, error) - TrackAccountStatus(publicKey string) (*api.OKResponse, error) + CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) + TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) } type AccountService struct { @@ -41,7 +41,7 @@ type TestAccountService struct { // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil -func (as *AccountService) CheckAccountStatus(trackingId string, ctx context.Context) (*models.TrackStatusResponse, error) { +func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { resp, err := http.Get(config.BalanceURL + trackingId) if err != nil { return nil, err @@ -62,7 +62,7 @@ func (as *AccountService) CheckAccountStatus(trackingId string, ctx context.Cont } -func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { +func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { var err error // Construct the URL with the path parameter url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) @@ -106,7 +106,7 @@ func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. -func (as *AccountService) CheckBalance(publicKey string, ctx context.Context) (*models.BalanceResponse, error) { +func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { resp, err := http.Get(config.BalanceURL + publicKey) if err != nil { return nil, err @@ -178,7 +178,7 @@ func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKRespon } -func (tas *TestAccountService) CheckBalance(publicKey string, ctx context.Context) (*models.BalanceResponse, error) { +func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { balanceResponse := &models.BalanceResponse{ Ok: true, Result: struct { @@ -192,7 +192,7 @@ func (tas *TestAccountService) CheckBalance(publicKey string, ctx context.Contex return balanceResponse, nil } -func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { +func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { return &api.OKResponse{ Ok: true, Description: "Account creation succeeded", @@ -202,17 +202,7 @@ func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResp }, nil } -func (tas *TestAccountService) TrackAccountStatus(publicKey ,) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": true, - }, - }, nil -} - -func (tas *TestAccountService) CheckAccountStatus(trackingId string, ctx context.Context) (*models.TrackStatusResponse, error) { +func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { trackResponse := &models.TrackStatusResponse{ Ok: true, Result: struct { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4c03d7b..cb9f8fd 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -544,7 +544,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b if err != nil { return res, err } - okResponse, err = h.accountService.TrackAccountStatus(string(publicKey),ctx) + okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err @@ -651,7 +651,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - balanceResponse, err := h.accountService.CheckBalance(string(publicKey),ctx) + balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) if err != nil { return res, nil } @@ -684,7 +684,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, err } - balanceResponse, err := h.accountService.CheckBalance(string(publicKey),ctx) + balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) if err != nil { return res, nil } @@ -802,7 +802,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res store := h.userdataStore publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) - balanceResp, err := h.accountService.CheckBalance(string(publicKey),ctx) + balanceResp, err := h.accountService.CheckBalance(ctx, string(publicKey)) if err != nil { return res, nil } @@ -832,7 +832,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) amountStr := string(input) - balanceRes, err := h.accountService.CheckBalance(string(publicKey),ctx) + balanceRes, err := h.accountService.CheckBalance(ctx, string(publicKey)) balanceStr := balanceRes.Result.Balance if !balanceRes.Ok { diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index b7d44e7..9aab44d 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -18,17 +18,17 @@ func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse return args.Get(0).(*api.OKResponse), args.Error(1) } -func (m *MockAccountService) CheckBalance(publicKey string,ctx context.Context) (*models.BalanceResponse, error) { +func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { args := m.Called(publicKey) return args.Get(0).(*models.BalanceResponse), args.Error(1) } -func (m *MockAccountService) CheckAccountStatus(trackingId string,ctx context.Context) (*models.TrackStatusResponse, error) { +func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { args := m.Called(trackingId) return args.Get(0).(*models.TrackStatusResponse), args.Error(1) } -func (m *MockAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { +func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey string) (*api.OKResponse, error) { args := m.Called(publicKey) return args.Get(0).(*api.OKResponse), args.Error(1) } From 6e7b46666e13525065d7005b9918bb3eb2eb0e03 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 24 Oct 2024 17:34:42 +0300 Subject: [PATCH 145/175] ensure mod match with master --- go.mod | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 76aaf74..7112503 100644 --- a/go.mod +++ b/go.mod @@ -12,13 +12,7 @@ require ( ) -require ( - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.0 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/joho/godotenv v1.5.1 - github.com/kr/text v0.2.0 +require github.com/joho/godotenv v1.5.1 require ( github.com/grassrootseconomics/eth-custodial v1.3.0-beta @@ -32,11 +26,6 @@ require ( golang.org/x/sync v0.8.0 // indirect golang.org/x/text v0.18.0 // indirect ) - github.com/rogpeppe/go-internal v1.13.1 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect -) require ( github.com/alecthomas/participle/v2 v2.0.0 // indirect @@ -55,3 +44,5 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) + + From 728815f0c6780022524d17e0c0f6a916f394cd58 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 17:50:37 +0300 Subject: [PATCH 146/175] Include the active symbol in the send menu --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 41 +++++++++++++++++++-- services/registration/amount | 2 +- services/registration/amount.vis | 7 ++-- services/registration/amount_swa | 2 +- services/registration/locale/swa/default.po | 4 +- 6 files changed, 46 insertions(+), 11 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index d14f7a7..311e694 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -70,6 +70,7 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance) ls.DbRs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient) ls.DbRs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset) + ls.DbRs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount) ls.DbRs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount) ls.DbRs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount) ls.DbRs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a9cb5b7..56080bb 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -785,6 +785,28 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input return res, nil } +// MaxAmount gets the current balance from the API and sets it as +// the result content. +func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + + activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + if err != nil { + return res, err + } + + res.Content = string(activeBal) + + return res, nil +} + // ValidateAmount ensures that the given input is a valid amount and that // it is not more than the current balance. func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { @@ -824,12 +846,14 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) return res, nil } - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(amountStr)) + // Format the amount with 2 decimal places before saving + formattedAmount := fmt.Sprintf("%.2f", inputAmount) + err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(formattedAmount)) if err != nil { return res, err } - res.Content = fmt.Sprintf("%.3f", inputAmount) + res.Content = fmt.Sprintf("%s", formattedAmount) return res, nil } @@ -872,9 +896,16 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res return res, fmt.Errorf("missing session") } store := h.userdataStore + + // retrieve the active symbol + activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + if err != nil { + return res, err + } + amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) - res.Content = string(amount) + res.Content = fmt.Sprintf("%s %s", string(amount), string(activeSym)) return res, nil } @@ -900,7 +931,9 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) - res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(sessionId)) + activeSym, _ := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + + res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipient), string(amount), string(activeSym), string(sessionId)) account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") if err != nil { diff --git a/services/registration/amount b/services/registration/amount index d3e8602..9142aba 100644 --- a/services/registration/amount +++ b/services/registration/amount @@ -1,2 +1,2 @@ -Maximum amount: {{.check_balance}} +Maximum amount: {{.max_amount}} Enter amount: \ No newline at end of file diff --git a/services/registration/amount.vis b/services/registration/amount.vis index 7c2a0a7..82e1fd4 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -1,6 +1,7 @@ LOAD reset_transaction_amount 0 -LOAD check_balance 48 -MAP check_balance +LOAD max_amount 10 +RELOAD max_amount +MAP max_amount MOUT back 0 HALT LOAD validate_amount 64 @@ -10,5 +11,5 @@ CATCH invalid_amount flag_invalid_amount 1 INCMP _ 0 LOAD get_recipient 12 LOAD get_sender 64 -LOAD get_amount 12 +LOAD get_amount 32 INCMP transaction_pin * diff --git a/services/registration/amount_swa b/services/registration/amount_swa index d071f63..0c8cf01 100644 --- a/services/registration/amount_swa +++ b/services/registration/amount_swa @@ -1,2 +1,2 @@ -Kiwango cha juu: {{.check_balance}} +Kiwango cha juu: {{.max_amount}} Weka kiwango: \ No newline at end of file diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index 0a3909b..ba9a9bb 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -1,8 +1,8 @@ msgid "Your account balance is %s" msgstr "Salio lako ni %s" -msgid "Your request has been sent. %s will receive %s from %s." -msgstr "Ombi lako limetumwa. %s atapokea %s kutoka kwa %s." +msgid "Your request has been sent. %s will receive %s %s from %s." +msgstr "Ombi lako limetumwa. %s atapokea %s %s kutoka kwa %s." msgid "Thank you for using Sarafu. Goodbye!" msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!" From a92c640cb7ab15c16507d1ff5ae7019cc8a7e52d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 18:15:54 +0300 Subject: [PATCH 147/175] Updated tests --- internal/handlers/ussd/menuhandler_test.go | 53 ++++++++++++---------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index e86167b..97df63e 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -494,26 +494,30 @@ func TestGetSender(t *testing.T) { } func TestGetAmount(t *testing.T) { - mockStore := new(mocks.MockUserDataStore) + mockDataStore := new(mocks.MockUserDataStore) // Define test data sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) - Amount := "0.03CELO" + amount := "0.03" + activeSym := "SRF" // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return([]byte(Amount), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(activeSym), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return([]byte(amount), nil) // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: mockDataStore, } // Call the method - res, _ := h.GetAmount(ctx, "get_amount", []byte("Getting amount...")) + res, _ := h.GetAmount(ctx, "get_amount", []byte("")) + + formattedAmount := fmt.Sprintf("%s %s", amount, activeSym) //Assert that the retrieved amount is what was set as the content - assert.Equal(t, Amount, res.Content) + assert.Equal(t, formattedAmount, res.Content) } @@ -1276,16 +1280,18 @@ func TestInitiateTransaction(t *testing.T) { input []byte Recipient []byte Amount []byte + ActiveSym []byte status string expectedResult resource.Result }{ { name: "Test initiate transaction", - Amount: []byte("0.002 CELO"), + Amount: []byte("0.002"), + ActiveSym: []byte("SRF"), Recipient: []byte("0x12415ass27192"), expectedResult: resource.Result{ FlagReset: []uint32{account_authorized_flag}, - Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 CELO from 254712345678.", + Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 SRF from 254712345678.", }, }, } @@ -1294,6 +1300,7 @@ func TestInitiateTransaction(t *testing.T) { // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(tt.ActiveSym, nil) // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1444,28 +1451,28 @@ func TestValidateAmount(t *testing.T) { }{ { name: "Test with valid amount", - input: []byte("0.001"), - activeBal: []byte("0.003"), + input: []byte("4.10"), + activeBal: []byte("5"), expectedResult: resource.Result{ - Content: "0.001", + Content: "4.10", }, }, { name: "Test with amount larger than active balance", - input: []byte("0.02"), - activeBal: []byte("0.003"), + input: []byte("5.02"), + activeBal: []byte("5"), expectedResult: resource.Result{ - FlagSet: []uint32{flag_invalid_amount}, - Content: "0.02", + FlagSet: []uint32{flag_invalid_amount}, + Content: "5.02", }, }, { - name: "Test with invalid amount format", - input: []byte("0.02ms"), - balance: "0.003 CELO", + name: "Test with invalid amount format", + input: []byte("0.02ms"), + activeBal: []byte("5"), expectedResult: resource.Result{ - FlagSet: []uint32{flag_invalid_amount}, - Content: "0.02ms", + FlagSet: []uint32{flag_invalid_amount}, + Content: "0.02ms", }, }, } @@ -1567,7 +1574,7 @@ func TestCheckBalance(t *testing.T) { publicKey: "0X98765432109", activeSym: "ETH", activeBal: "1.5", - expectedResult: resource.Result{Content: "1.5 ETH"}, + expectedResult: resource.Result{Content: "Balance: 1.5 ETH\n"}, expectError: false, }, } @@ -1582,11 +1589,11 @@ func TestCheckBalance(t *testing.T) { userdataStore: mockDataStore, accountService: mockAccountService, } - + // Mock for user with active sym mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) - + res, err := h.CheckBalance(ctx, "check_balance", []byte("")) if tt.expectError { From d113ea82fdd41b844bcef380b0288946ac3679be Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 20:21:28 +0300 Subject: [PATCH 148/175] Add dynamic send_amount and session_id --- menutraversal_test/menu_traversal_test.go | 22 +++++++++++++++++----- menutraversal_test/test_setup.json | 8 ++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index 2b2a093..900ebcc 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -56,11 +56,22 @@ func extractBalance(response []byte) string { // Extracts the Maximum amount value from the engine response. func extractMaxAmount(response []byte) string { - // Regex to match "Maximum amount: " followed by a newline - re := regexp.MustCompile(`(?m)^Maximum amount:\s+(\d+(\.\d+)?)\s+([A-Z]+)`) + // Regex to match "Maximum amount: " followed by a newline + re := regexp.MustCompile(`(?m)^Maximum amount:\s+(\d+(\.\d+)?)`) match := re.FindSubmatch(response) if match != nil { - return string(match[1]) + " " + string(match[3]) // " " + return string(match[1]) // "" + } + return "" +} + +// Extracts the send amount value from the engine response. +func extractSendAmount(response []byte) string { + // Regex to match the pattern "will receive X.XX SYM from" + re := regexp.MustCompile(`will receive (\d+\.\d{2}\s+[A-Z]+) from`) + match := re.FindSubmatch(response) + if match != nil { + return string(match[1]) // Returns "X.XX SYM" } return "" } @@ -304,12 +315,13 @@ func TestMainMenuSend(t *testing.T) { b := w.Bytes() balance := extractBalance(b) max_amount := extractMaxAmount(b) - publicKey := extractPublicKey(b) + send_amount := extractSendAmount(b) expectedContent := []byte(step.ExpectedContent) expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) expectedContent = bytes.Replace(expectedContent, []byte("{max_amount}"), []byte(max_amount), -1) - expectedContent = bytes.Replace(expectedContent, []byte("{public_key}"), []byte(publicKey), -1) + expectedContent = bytes.Replace(expectedContent, []byte("{send_amount}"), []byte(send_amount), -1) + expectedContent = bytes.Replace(expectedContent, []byte("{session_id}"), []byte(sessionID), -1) step.ExpectedContent = string(expectedContent) match, err := step.MatchesExpectedContent(b) diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 736d4de..13166a4 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -84,8 +84,8 @@ "expectedContent": "{max_amount}\nEnter amount:\n0:Back" }, { - "input": "0.001", - "expectedContent": "065656 will receive 0.001 from {public_key}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" + "input": "1.00", + "expectedContent": "065656 will receive {send_amount} from {session_id}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" }, { "input": "1222", @@ -93,11 +93,11 @@ }, { "input": "1", - "expectedContent": "065656 will receive 0.001 from {public_key}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" + "expectedContent": "065656 will receive {send_amount} from {session_id}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" }, { "input": "1234", - "expectedContent": "Your request has been sent. 065656 will receive 0.001 from {public_key}." + "expectedContent": "Your request has been sent. 065656 will receive {send_amount} from {session_id}." } ] }, From b4454f7517070d1f1f3a121e5b8769df5243e974 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 24 Oct 2024 20:44:29 +0300 Subject: [PATCH 149/175] Added context to FetchVouchers --- internal/handlers/server/accountservice.go | 6 +++--- internal/handlers/ussd/menuhandler.go | 4 ++-- internal/mocks/servicemock.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 58774ce..3e2f91d 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -25,7 +25,7 @@ type AccountServiceInterface interface { CreateAccount(ctx context.Context) (*api.OKResponse, error) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) - FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) + FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) } type AccountService struct { @@ -174,7 +174,7 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, e // FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint // Parameters: // - publicKey: The public key associated with the account. -func (as *AccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { +func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { file, err := os.Open("sample_tokens.json") if err != nil { return nil, err @@ -245,7 +245,7 @@ func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingI return trackResponse, nil } -func (tas *TestAccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { +func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { return &models.VoucherHoldingResponse{ Ok: true, Result: struct { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ec0a5b3..f4d279b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1035,7 +1035,7 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by } // Fetch vouchers from the API using the public key - vouchersResp, err := h.accountService.FetchVouchers(string(publicKey)) + vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) if err != nil { return res, nil } @@ -1089,7 +1089,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) } // Fetch vouchers from the API using the public key - vouchersResp, err := h.accountService.FetchVouchers(string(publicKey)) + vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) if err != nil { return res, nil } diff --git a/internal/mocks/servicemock.go b/internal/mocks/servicemock.go index 6a217ce..de0e99a 100644 --- a/internal/mocks/servicemock.go +++ b/internal/mocks/servicemock.go @@ -34,7 +34,7 @@ func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey st } -func (m *MockAccountService) FetchVouchers(publicKey string) (*models.VoucherHoldingResponse, error) { +func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { args := m.Called(publicKey) return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) } From a3be98c5144f46171a970a044ceff0114d8a9337 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 25 Oct 2024 09:36:43 +0300 Subject: [PATCH 150/175] update test account service --- .../testservice/TestAccountService.go | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 40c3886..81f66ef 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -1,31 +1,29 @@ package testservice import ( + "context" "encoding/json" "time" "git.grassecon.net/urdt/ussd/internal/models" + "github.com/grassrootseconomics/eth-custodial/pkg/api" ) type TestAccountService struct { } -func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { - return &models.AccountResponse{ - Ok: true, - Result: struct { - CustodialId json.Number `json:"custodialId"` - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` - }{ - CustodialId: json.Number("182"), - PublicKey: "0x48ADca309b5085852207FAaf2816eD72B52F527C", - TrackingId: "28ebe84d-b925-472c-87ae-bbdfa1fb97be", +func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { + return &api.OKResponse{ + Ok: true, + Description: "Account creation succeeded", + Result: map[string]any{ + "trackingId": "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", + "publicKey": "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", }, }, nil } -func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { +func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { balanceResponse := &models.BalanceResponse{ Ok: true, Result: struct { @@ -40,7 +38,7 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe return balanceResponse, nil } -func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { +func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { trackResponse := &models.TrackStatusResponse{ Ok: true, Result: struct { @@ -63,3 +61,13 @@ func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.Tr } return trackResponse, nil } + +func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { + return &api.OKResponse{ + Ok: true, + Description: "Account creation succeeded", + Result: map[string]any{ + "active": true, + }, + }, nil +} From a0e97cfe5b07638d37fdb57a03333001ee81f4da Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 25 Oct 2024 09:37:16 +0300 Subject: [PATCH 151/175] remove test account service implementation --- internal/handlers/server/accountservice.go | 60 ---------------------- 1 file changed, 60 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index a861e71..93e57a3 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -28,9 +28,6 @@ type AccountServiceInterface interface { type AccountService struct { } -type TestAccountService struct { -} - // Parameters: // - trackingId: A unique identifier for the account.This should be obtained from a previous call to // CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the @@ -167,60 +164,3 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, e } return &okResponse, nil } - -func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation request received successfully", - Result: map[string]any{"publicKey": "0x48ADca309b5085852207FAaf2816eD72B52F527C", "trackingId": "28ebe84d-b925-472c-87ae-bbdfa1fb97be"}, - }, nil - -} - -func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { - balanceResponse := &models.BalanceResponse{ - Ok: true, - Result: struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - }{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, - } - return balanceResponse, nil -} - -func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": true, - }, - }, nil -} - -func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { - trackResponse := &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - } - return trackResponse, nil -} From 3792bfdc1f0250122d77b13001ef03f84d2cc996 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 25 Oct 2024 09:38:09 +0300 Subject: [PATCH 152/175] run mod tidy --- go.mod | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7112503..0b30354 100644 --- a/go.mod +++ b/go.mod @@ -7,19 +7,17 @@ toolchain go1.23.2 require ( git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b github.com/alecthomas/assert/v2 v2.2.2 + github.com/grassrootseconomics/eth-custodial v1.3.0-beta github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 - ) -require github.com/joho/godotenv v1.5.1 - require ( - github.com/grassrootseconomics/eth-custodial v1.3.0-beta github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.7.1 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/joho/godotenv v1.5.1 github.com/kr/text v0.2.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect golang.org/x/crypto v0.27.0 // indirect @@ -42,7 +40,4 @@ require ( github.com/stretchr/testify v1.9.0 github.com/x448/float16 v0.8.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - ) - - From 3ef64271e7429d00012c579155a20c97784ec38d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 25 Oct 2024 17:24:09 +0300 Subject: [PATCH 153/175] Store and retrieve the voucher decimals and contract address --- internal/handlers/ussd/menuhandler.go | 284 +++++++++++++++++--------- internal/utils/db.go | 4 + 2 files changed, 187 insertions(+), 101 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f4d279b..ec33ce6 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -57,6 +57,14 @@ func (fm *FlagManager) GetFlag(label string) (uint32, error) { return fm.parser.GetFlag(label) } +// VoucherMetadata helps organize voucher data fields +type VoucherMetadata struct { + Symbol string + Balance string + Decimal string + Address string +} + type Handlers struct { pe *persist.Persister st *state.State @@ -1094,41 +1102,49 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - // process voucher data - voucherSymbolList, voucherBalanceList := ProcessVouchers(vouchersResp.Result.Holdings) - prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) - err = prefixdb.Put(ctx, []byte("sym"), []byte(voucherSymbolList)) - if err != nil { - return res, nil + data := processVouchers(vouchersResp.Result.Holdings) + + // Store all voucher data + dataMap := map[string]string{ + "sym": data.Symbol, + "bal": data.Balance, + "deci": data.Decimal, + "addr": data.Address, } - err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList)) - if err != nil { - return res, nil + for key, value := range dataMap { + if err := prefixdb.Put(ctx, []byte(key), []byte(value)); err != nil { + return res, nil + } } return res, nil } -// ProcessVouchers formats the holdings into symbol and balance lists. -func ProcessVouchers(holdings []struct { +// processVouchers converts holdings into formatted strings +func processVouchers(holdings []struct { ContractAddress string `json:"contractAddress"` TokenSymbol string `json:"tokenSymbol"` TokenDecimals string `json:"tokenDecimals"` Balance string `json:"balance"` -}) (string, string) { - var numberedSymbols, numberedBalances []string +}) VoucherMetadata { + var data VoucherMetadata + var symbols, balances, decimals, addresses []string - for i, voucher := range holdings { - numberedSymbols = append(numberedSymbols, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol)) - numberedBalances = append(numberedBalances, fmt.Sprintf("%d:%s", i+1, voucher.Balance)) + for i, h := range holdings { + symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) + balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance)) + decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals)) + addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress)) } - voucherSymbolList := strings.Join(numberedSymbols, "\n") - voucherBalanceList := strings.Join(numberedBalances, "\n") + data.Symbol = strings.Join(symbols, "\n") + data.Balance = strings.Join(balances, "\n") + data.Decimal = strings.Join(decimals, "\n") + data.Address = strings.Join(addresses, "\n") - return voucherSymbolList, voucherBalanceList + return data } // GetVoucherList fetches the list of vouchers and formats them @@ -1162,7 +1178,6 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") inputStr := string(input) - if inputStr == "0" || inputStr == "99" { res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) return res, nil @@ -1170,118 +1185,185 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) - // Retrieve the voucher symbol list - voucherSymbolList, err := prefixdb.Get(ctx, []byte("sym")) + // Retrieve the voucher metadata + metadata, err := getVoucherData(ctx, prefixdb, inputStr) if err != nil { - return res, fmt.Errorf("failed to retrieve voucher symbol list: %v", err) + return res, fmt.Errorf("failed to retrieve voucher data: %v", err) } - // Retrieve the voucher balance list - voucherBalanceList, err := prefixdb.Get(ctx, []byte("bal")) - if err != nil { - return res, fmt.Errorf("failed to retrieve voucher balance list: %v", err) - } - - // match the voucher symbol and balance with the input - matchedSymbol, matchedBalance := MatchVoucher(inputStr, string(voucherSymbolList), string(voucherBalanceList)) - - // If a match is found, write the temporary sym and balance - if matchedSymbol != "" && matchedBalance != "" { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) - if err != nil { - return res, err - } - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance)) - if err != nil { - return res, err - } - - res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) - res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance) - } else { + if metadata == nil { res.FlagSet = append(res.FlagSet, flag_incorrect_voucher) + return res, nil } + if err := h.storeTemporaryVoucher(ctx, sessionId, metadata); err != nil { + return resource.Result{}, err + } + + res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) + res.Content = fmt.Sprintf("%s\n%s", metadata.Symbol, metadata.Balance) + return res, nil } -// MatchVoucher finds the matching voucher symbol and balance based on the input. -func MatchVoucher(inputStr string, voucherSymbols, voucherBalances string) (string, string) { - // Split the lists into slices for processing - symbols := strings.Split(voucherSymbols, "\n") - balances := strings.Split(voucherBalances, "\n") +// getVoucherData retrieves and matches voucher data +func getVoucherData(ctx context.Context, db *storage.SubPrefixDb, input string) (*VoucherMetadata, error) { + keys := []string{"sym", "bal", "deci", "addr"} + data := make(map[string]string) - var matchedSymbol, matchedBalance string + for _, key := range keys { + value, err := db.Get(ctx, []byte(key)) + if err != nil { + return nil, fmt.Errorf("failed to get %s: %v", key, err) + } + data[key] = string(value) + } - for i, symbol := range symbols { - symbolParts := strings.SplitN(symbol, ":", 2) - if len(symbolParts) != 2 { + symbol, balance, decimal, address := matchVoucher(input, + data["sym"], + data["bal"], + data["deci"], + data["addr"]) + + if symbol == "" { + return nil, nil + } + + return &VoucherMetadata{ + Symbol: symbol, + Balance: balance, + Decimal: decimal, + Address: address, + }, nil +} + +// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input. +func matchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) { + symList := strings.Split(symbols, "\n") + balList := strings.Split(balances, "\n") + decList := strings.Split(decimals, "\n") + addrList := strings.Split(addresses, "\n") + + for i, sym := range symList { + parts := strings.SplitN(sym, ":", 2) + if len(parts) != 2 { continue } - voucherNum := symbolParts[0] - voucherSymbol := symbolParts[1] - // Check if input matches either the number or the symbol - if inputStr == voucherNum || strings.EqualFold(inputStr, voucherSymbol) { - matchedSymbol = voucherSymbol - // Ensure there's a corresponding balance - if i < len(balances) { - matchedBalance = strings.SplitN(balances[i], ":", 2)[1] + if input == parts[0] || strings.EqualFold(input, parts[1]) { + symbol = parts[1] + if i < len(balList) { + balance = strings.SplitN(balList[i], ":", 2)[1] + } + if i < len(decList) { + decimal = strings.SplitN(decList[i], ":", 2)[1] + } + if i < len(addrList) { + address = strings.SplitN(addrList[i], ":", 2)[1] } break } } - - return matchedSymbol, matchedBalance + return +} + +func (h *Handlers) storeTemporaryVoucher(ctx context.Context, sessionId string, data *VoucherMetadata) error { + entries := map[utils.DataTyp][]byte{ + utils.DATA_TEMPORARY_SYM: []byte(data.Symbol), + utils.DATA_TEMPORARY_BAL: []byte(data.Balance), + utils.DATA_TEMPORARY_DECIMAL: []byte(data.Decimal), + utils.DATA_TEMPORARY_ADDRESS: []byte(data.Address), + } + + for key, value := range entries { + if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + return nil } -// SetVoucher retrieves the temporary sym and balance, -// sets them as the active data and -// clears the temporary data func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error - store := h.userdataStore - + sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - // get the current temporary symbol - temporarySym, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM) + // Get temporary data + tempData, err := h.getTemporaryVoucherData(ctx, sessionId) if err != nil { - return res, err - } - // get the current temporary balance - temporaryBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL) - if err != nil { - return res, err + return resource.Result{}, err } - // set the active symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym)) - if err != nil { - return res, err - } - // set the active balance - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(temporaryBal)) - if err != nil { - return res, err + // Set as active and clear temporary + if err := h.updateVoucherData(ctx, sessionId, tempData); err != nil { + return resource.Result{}, err } - // reset the temporary symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")) - if err != nil { - return res, err - } - // reset the temporary balance - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")) - if err != nil { - return res, err - } - - res.Content = string(temporarySym) - - return res, nil + return resource.Result{Content: tempData.Symbol}, nil +} + +func (h *Handlers) getTemporaryVoucherData(ctx context.Context, sessionId string) (*VoucherMetadata, error) { + store := h.userdataStore + + keys := []utils.DataTyp{ + utils.DATA_TEMPORARY_SYM, + utils.DATA_TEMPORARY_BAL, + utils.DATA_TEMPORARY_DECIMAL, + utils.DATA_TEMPORARY_ADDRESS, + } + + data := &VoucherMetadata{} + values := make([][]byte, len(keys)) + + for i, key := range keys { + value, err := store.ReadEntry(ctx, sessionId, key) + if err != nil { + return nil, err + } + values[i] = value + } + + data.Symbol = string(values[0]) + data.Balance = string(values[1]) + data.Decimal = string(values[2]) + data.Address = string(values[3]) + + return data, nil +} + +func (h *Handlers) updateVoucherData(ctx context.Context, sessionId string, data *VoucherMetadata) error { + // Set active voucher data + activeEntries := map[utils.DataTyp][]byte{ + utils.DATA_ACTIVE_SYM: []byte(data.Symbol), + utils.DATA_ACTIVE_BAL: []byte(data.Balance), + utils.DATA_ACTIVE_DECIMAL: []byte(data.Decimal), + utils.DATA_ACTIVE_ADDRESS: []byte(data.Address), + } + + // Clear temporary voucher data + tempEntries := map[utils.DataTyp][]byte{ + utils.DATA_TEMPORARY_SYM: []byte(""), + utils.DATA_TEMPORARY_BAL: []byte(""), + utils.DATA_TEMPORARY_DECIMAL: []byte(""), + utils.DATA_TEMPORARY_ADDRESS: []byte(""), + } + + // Write all entries + for key, value := range activeEntries { + if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + + for key, value := range tempEntries { + if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + + return nil } diff --git a/internal/utils/db.go b/internal/utils/db.go index 45e7681..3c2c81e 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -28,6 +28,10 @@ const ( DATA_ACTIVE_SYM DATA_TEMPORARY_BAL DATA_ACTIVE_BAL + DATA_TEMPORARY_DECIMAL + DATA_ACTIVE_DECIMAL + DATA_TEMPORARY_ADDRESS + DATA_ACTIVE_ADDRESS ) func typToBytes(typ DataTyp) []byte { From 4e1b2d5ddb13e6cec13bc82515c53df72670c79b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 25 Oct 2024 18:01:52 +0300 Subject: [PATCH 154/175] update account services --- internal/handlers/server/accountservice.go | 86 ------------------- .../testservice/TestAccountService.go | 28 ++++++ 2 files changed, 28 insertions(+), 86 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 8ef909d..890d1db 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "os" - "time" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" @@ -184,88 +183,3 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( } return &holdings, nil } - -func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation request received successfully", - Result: map[string]any{"publicKey": "0x48ADca309b5085852207FAaf2816eD72B52F527C", "trackingId": "28ebe84d-b925-472c-87ae-bbdfa1fb97be"}, - }, nil - -} - -func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { - balanceResponse := &models.BalanceResponse{ - Ok: true, - Result: struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - }{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, - } - return balanceResponse, nil -} - -func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": true, - }, - }, nil -} - -func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { - trackResponse := &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - } - return trackResponse, nil -} - -func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { - return &models.VoucherHoldingResponse{ - Ok: true, - Result: struct { - Holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - } `json:"holdings"` - }{ - Holdings: []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{ - { - ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", - TokenSymbol: "SRF", - TokenDecimals: "6", - Balance: "2745987", - }, - }, - }, - }, nil -} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 81f66ef..6332345 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -71,3 +71,31 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey }, }, nil } + +func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { + return &models.VoucherHoldingResponse{ + Ok: true, + Result: struct { + Holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + } `json:"holdings"` + }{ + Holdings: []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{ + { + ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", + TokenSymbol: "SRF", + TokenDecimals: "6", + Balance: "2745987", + }, + }, + }, + }, nil +} From ddae746b9d3e0e9b9470040e48d210de0d65ceeb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 11:07:59 +0300 Subject: [PATCH 155/175] formatted code --- internal/handlers/ussd/menuhandler.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ec33ce6..1385261 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1219,10 +1219,10 @@ func getVoucherData(ctx context.Context, db *storage.SubPrefixDb, input string) data[key] = string(value) } - symbol, balance, decimal, address := matchVoucher(input, - data["sym"], - data["bal"], - data["deci"], + symbol, balance, decimal, address := matchVoucher(input, + data["sym"], + data["bal"], + data["deci"], data["addr"]) if symbol == "" { @@ -1286,7 +1286,7 @@ func (h *Handlers) storeTemporaryVoucher(ctx context.Context, sessionId string, func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error - + sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") @@ -1308,7 +1308,7 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re func (h *Handlers) getTemporaryVoucherData(ctx context.Context, sessionId string) (*VoucherMetadata, error) { store := h.userdataStore - + keys := []utils.DataTyp{ utils.DATA_TEMPORARY_SYM, utils.DATA_TEMPORARY_BAL, From dc782d87a8a0cf3e324fc9f3ce7b93b01a420803 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 11:20:06 +0300 Subject: [PATCH 156/175] Updated the TestSetVoucher --- internal/handlers/ussd/menuhandler.go | 9 +++--- internal/handlers/ussd/menuhandler_test.go | 37 +++++++++++++++++++--- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 1385261..f04b3f8 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1197,7 +1197,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } if err := h.storeTemporaryVoucher(ctx, sessionId, metadata); err != nil { - return resource.Result{}, err + return res, err } res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) @@ -1295,15 +1295,16 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re // Get temporary data tempData, err := h.getTemporaryVoucherData(ctx, sessionId) if err != nil { - return resource.Result{}, err + return res, err } // Set as active and clear temporary if err := h.updateVoucherData(ctx, sessionId, tempData); err != nil { - return resource.Result{}, err + return res, err } - return resource.Result{Content: tempData.Symbol}, nil + res.Content = tempData.Symbol + return res, nil } func (h *Handlers) getTemporaryVoucherData(ctx context.Context, sessionId string) (*VoucherMetadata, error) { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 9bcee63..b53a289 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1816,16 +1816,43 @@ func TestSetVoucher(t *testing.T) { sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) + // Define the temporary voucher data temporarySym := []byte("tempSym") temporaryBal := []byte("tempBal") + temporaryDecimal := []byte("2") + temporaryAddress := []byte("0x123456") - // Set expectations for the mock data store + // Define the expected active entries + activeEntries := map[utils.DataTyp][]byte{ + utils.DATA_ACTIVE_SYM: temporarySym, + utils.DATA_ACTIVE_BAL: temporaryBal, + utils.DATA_ACTIVE_DECIMAL: temporaryDecimal, + utils.DATA_ACTIVE_ADDRESS: temporaryAddress, + } + + // Define the temporary entries to be cleared + tempEntries := map[utils.DataTyp][]byte{ + utils.DATA_TEMPORARY_SYM: []byte(""), + utils.DATA_TEMPORARY_BAL: []byte(""), + utils.DATA_TEMPORARY_DECIMAL: []byte(""), + utils.DATA_TEMPORARY_ADDRESS: []byte(""), + } + + // Mocking ReadEntry calls for temporary data retrieval mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return(temporarySym, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return(temporaryBal, nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, temporarySym).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, temporaryBal).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return(temporaryDecimal, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return(temporaryAddress, nil) + + // Mocking WriteEntry calls for setting active data + for key, value := range activeEntries { + mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) + } + + // Mocking WriteEntry calls for clearing temporary data + for key, value := range tempEntries { + mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) + } h := &Handlers{ userdataStore: mockDataStore, From 5d294b663cc1fc3621eaba97b9622f41b44021a5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 16:21:31 +0300 Subject: [PATCH 157/175] Added the PrefixDb interface --- internal/storage/db.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/storage/db.go b/internal/storage/db.go index 060f0c0..8c9ff35 100644 --- a/internal/storage/db.go +++ b/internal/storage/db.go @@ -10,6 +10,14 @@ const ( DATATYPE_USERSUB = 64 ) +// PrefixDb interface abstracts the database operations. +type PrefixDb interface { + Get(ctx context.Context, key []byte) ([]byte, error) + Put(ctx context.Context, key []byte, val []byte) error +} + +var _ PrefixDb = (*SubPrefixDb)(nil) + type SubPrefixDb struct { store db.Db pfx []byte @@ -29,11 +37,7 @@ func (s *SubPrefixDb) toKey(k []byte) []byte { func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { s.store.SetPrefix(DATATYPE_USERSUB) key = s.toKey(key) - v, err := s.store.Get(ctx, key) - if err != nil { - return nil, err - } - return v, nil + return s.store.Get(ctx, key) } func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error { From 520f5abdcdba081171b2ed2cdc835a91603c38d3 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 16:23:44 +0300 Subject: [PATCH 158/175] Added the subPrefixDb mock --- internal/mocks/subprefixdbmock.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 internal/mocks/subprefixdbmock.go diff --git a/internal/mocks/subprefixdbmock.go b/internal/mocks/subprefixdbmock.go new file mode 100644 index 0000000..e98bcb6 --- /dev/null +++ b/internal/mocks/subprefixdbmock.go @@ -0,0 +1,21 @@ +package mocks + +import ( + "context" + + "github.com/stretchr/testify/mock" +) + +type MockSubPrefixDb struct { + mock.Mock +} + +func (m *MockSubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { + args := m.Called(ctx, key) + return args.Get(0).([]byte), args.Error(1) +} + +func (m *MockSubPrefixDb) Put(ctx context.Context, key, val []byte) error { + args := m.Called(ctx, key, val) + return args.Error(0) +} From 131f3bcf46861d5424246b26ddb9f60515eba65a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 16:27:24 +0300 Subject: [PATCH 159/175] Added the prefixDb to the Handlers struct~ --- internal/handlers/ussd/menuhandler.go | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f04b3f8..59377d4 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -72,6 +72,7 @@ type Handlers struct { userdataStore utils.DataStore flagManager *asm.FlagParser accountService server.AccountServiceInterface + prefixDb storage.PrefixDb } func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { @@ -81,10 +82,14 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s userDb := &utils.UserDataStore{ Db: userdataStore, } + // Instantiate the SubPrefixDb with "vouchers" prefix + prefixDb := storage.NewSubPrefixDb(userdataStore, []byte("vouchers")) + h := &Handlers{ userdataStore: userDb, flagManager: appFlags, accountService: accountService, + prefixDb: prefixDb, } return h, nil } @@ -1102,7 +1107,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) data := processVouchers(vouchersResp.Result.Holdings) // Store all voucher data @@ -1114,7 +1118,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) } for key, value := range dataMap { - if err := prefixdb.Put(ctx, []byte(key), []byte(value)); err != nil { + if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil { return res, nil } } @@ -1152,12 +1156,9 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) var res resource.Result // Read vouchers from the store - store := h.userdataStore - prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) - - voucherData, err := prefixdb.Get(ctx, []byte("sym")) + voucherData, err := h.prefixDb.Get(ctx, []byte("sym")) if err != nil { - return res, nil + return res, err } res.Content = string(voucherData) @@ -1168,8 +1169,6 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) // ViewVoucher retrieves the token holding and balance from the subprefixDB func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - store := h.userdataStore - sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") @@ -1183,10 +1182,8 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers")) - - // Retrieve the voucher metadata - metadata, err := getVoucherData(ctx, prefixdb, inputStr) + // Retrieve the voucher metadata using the PrefixDb interface + metadata, err := getVoucherData(ctx, h.prefixDb, inputStr) if err != nil { return res, fmt.Errorf("failed to retrieve voucher data: %v", err) } @@ -1207,7 +1204,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } // getVoucherData retrieves and matches voucher data -func getVoucherData(ctx context.Context, db *storage.SubPrefixDb, input string) (*VoucherMetadata, error) { +func getVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) { keys := []string{"sym", "bal", "deci", "addr"} data := make(map[string]string) From 2c361e5b96fe98e5c64964676cb2e6d4b57b8aac Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 16:30:13 +0300 Subject: [PATCH 160/175] Added tests related to the vouchers list --- internal/handlers/ussd/menuhandler_test.go | 518 ++++++++++++++++++--- 1 file changed, 455 insertions(+), 63 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index b53a289..801e44c 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1480,8 +1480,8 @@ func TestValidateAmount(t *testing.T) { }, }, { - name: "Test with invalid amount format", - input: []byte("0.02ms"), + name: "Test with invalid amount format", + input: []byte("0.02ms"), activeBal: []byte("5"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, @@ -1810,67 +1810,6 @@ func TestConfirmPin(t *testing.T) { } } -func TestSetVoucher(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Define the temporary voucher data - temporarySym := []byte("tempSym") - temporaryBal := []byte("tempBal") - temporaryDecimal := []byte("2") - temporaryAddress := []byte("0x123456") - - // Define the expected active entries - activeEntries := map[utils.DataTyp][]byte{ - utils.DATA_ACTIVE_SYM: temporarySym, - utils.DATA_ACTIVE_BAL: temporaryBal, - utils.DATA_ACTIVE_DECIMAL: temporaryDecimal, - utils.DATA_ACTIVE_ADDRESS: temporaryAddress, - } - - // Define the temporary entries to be cleared - tempEntries := map[utils.DataTyp][]byte{ - utils.DATA_TEMPORARY_SYM: []byte(""), - utils.DATA_TEMPORARY_BAL: []byte(""), - utils.DATA_TEMPORARY_DECIMAL: []byte(""), - utils.DATA_TEMPORARY_ADDRESS: []byte(""), - } - - // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return(temporarySym, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return(temporaryBal, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return(temporaryDecimal, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return(temporaryAddress, nil) - - // Mocking WriteEntry calls for setting active data - for key, value := range activeEntries { - mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) - } - - // Mocking WriteEntry calls for clearing temporary data - for key, value := range tempEntries { - mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) - } - - h := &Handlers{ - userdataStore: mockDataStore, - } - - // Call the method under test - res, err := h.SetVoucher(ctx, "someSym", []byte{}) - - // Assert that no errors occurred - assert.NoError(t, err) - - // Assert that the result content is correct - assert.Equal(t, string(temporarySym), res.Content) - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) -} - func TestFetchCustodialBalances(t *testing.T) { fm, err := NewFlagManager(flagsPath) if err != nil { @@ -1952,3 +1891,456 @@ func TestFetchCustodialBalances(t *testing.T) { }) } } + +func TestSetDefaultVoucher(t *testing.T) { + fm, err := NewFlagManager(flagsPath) + if err != nil { + t.Logf(err.Error()) + } + + flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher") + if err != nil { + t.Logf(err.Error()) + } + + // Define session ID and mock data + sessionId := "session123" + publicKey := "0X13242618721" + notFoundErr := db.ErrNotFound{} + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + tests := []struct { + name string + vouchersResp *models.VoucherHoldingResponse + expectedResult resource.Result + }{ + { + name: "Test set default voucher when no active voucher exists", + vouchersResp: &models.VoucherHoldingResponse{ + Ok: true, + Description: "Vouchers fetched successfully", + Result: struct { + Holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + } `json:"holdings"` + }{ + Holdings: []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{ + { + ContractAddress: "0x123", + TokenSymbol: "TOKEN1", + TokenDecimals: "18", + Balance: "100", + }, + }, + }, + }, + expectedResult: resource.Result{}, + }, + { + name: "Test no vouchers available", + vouchersResp: &models.VoucherHoldingResponse{ + Ok: true, + Description: "No vouchers available", + Result: struct { + Holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + } `json:"holdings"` + }{ + Holdings: []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{}, + }, + }, + expectedResult: resource.Result{ + FlagSet: []uint32{flag_no_active_voucher}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + mockAccountService := new(mocks.MockAccountService) + + h := &Handlers{ + userdataStore: mockDataStore, + accountService: mockAccountService, + flagManager: fm.parser, + } + + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(""), notFoundErr) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + + mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) + + if len(tt.vouchersResp.Result.Holdings) > 0 { + firstVoucher := tt.vouchersResp.Result.Holdings[0] + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(firstVoucher.TokenSymbol)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(firstVoucher.Balance)).Return(nil) + } + + res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("some-input")) + + assert.NoError(t, err) + + assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") + + mockDataStore.AssertExpectations(t) + mockAccountService.AssertExpectations(t) + }) + } +} + +func TestCheckVouchers(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + mockAccountService := new(mocks.MockAccountService) + mockSubPrefixDb := new(mocks.MockSubPrefixDb) + + sessionId := "session123" + publicKey := "0X13242618721" + + h := &Handlers{ + userdataStore: mockDataStore, + accountService: mockAccountService, + prefixDb: mockSubPrefixDb, + } + + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + + mockVouchersResponse := &models.VoucherHoldingResponse{} + mockVouchersResponse.Result.Holdings = []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{ + {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, + {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, + } + + mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil) + + mockSubPrefixDb.On("Put", ctx, []byte("sym"), []byte("1:SRF\n2:MILO")).Return(nil) + mockSubPrefixDb.On("Put", ctx, []byte("bal"), []byte("1:100\n2:200")).Return(nil) + mockSubPrefixDb.On("Put", ctx, []byte("deci"), []byte("1:6\n2:4")).Return(nil) + mockSubPrefixDb.On("Put", ctx, []byte("addr"), []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa")).Return(nil) + + _, err := h.CheckVouchers(ctx, "check_vouchers", []byte("")) + + assert.NoError(t, err) + + mockDataStore.AssertExpectations(t) + mockAccountService.AssertExpectations(t) +} + +func TestProcessVouchers(t *testing.T) { + holdings := []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{ + {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, + {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, + } + + expectedResult := VoucherMetadata{ + Symbol: "1:SRF\n2:MILO", + Balance: "1:100\n2:200", + Decimal: "1:6\n2:4", + Address: "1:0xd4c288865Ce\n2:0x41c188d63Qa", + } + + result := processVouchers(holdings) + + assert.Equal(t, expectedResult, result) +} + +func TestGetVoucherList(t *testing.T) { + mockSubPrefixDb := new(mocks.MockSubPrefixDb) + + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + h := &Handlers{ + prefixDb: mockSubPrefixDb, + } + + mockSubPrefixDb.On("Get", ctx, []byte("sym")).Return([]byte("1:SRF\n2:MILO"), nil) + + res, err := h.GetVoucherList(ctx, "", []byte("")) + assert.NoError(t, err) + assert.Contains(t, res.Content, "1:SRF\n2:MILO") + + mockSubPrefixDb.AssertExpectations(t) +} + +func TestViewVoucher(t *testing.T) { + fm, err := NewFlagManager(flagsPath) + if err != nil { + t.Logf(err.Error()) + } + mockDataStore := new(mocks.MockUserDataStore) + mockSubPrefixDb := new(mocks.MockSubPrefixDb) + + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + h := &Handlers{ + userdataStore: mockDataStore, + flagManager: fm.parser, + prefixDb: mockSubPrefixDb, + } + + // Define mock voucher data + mockVoucherData := map[string]string{ + "sym": "1:SRF", + "bal": "1:100", + "deci": "1:6", + "addr": "1:0xd4c288865Ce", + } + + for key, value := range mockVoucherData { + mockSubPrefixDb.On("Get", ctx, []byte(key)).Return([]byte(value), nil) + } + + // Set up expectations for mockDataStore + expectedData := map[utils.DataTyp]string{ + utils.DATA_TEMPORARY_SYM: "SRF", + utils.DATA_TEMPORARY_BAL: "100", + utils.DATA_TEMPORARY_DECIMAL: "6", + utils.DATA_TEMPORARY_ADDRESS: "0xd4c288865Ce", + } + + for dataType, dataValue := range expectedData { + mockDataStore.On("WriteEntry", ctx, sessionId, dataType, []byte(dataValue)).Return(nil) + } + + res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) + assert.NoError(t, err) + assert.Contains(t, res.Content, "SRF\n100") + + mockDataStore.AssertExpectations(t) + mockSubPrefixDb.AssertExpectations(t) +} + +func TestGetVoucherData(t *testing.T) { + mockSubPrefixDb := new(mocks.MockSubPrefixDb) + ctx := context.Background() + + // Mocked voucher data + mockData := map[string][]byte{ + "sym": []byte("1:SRF\n2:MILO"), + "bal": []byte("1:100\n2:200"), + "deci": []byte("1:6\n2:4"), + "addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), + } + + // Mock Get calls + for key, value := range mockData { + mockSubPrefixDb.On("Get", ctx, []byte(key)).Return(value, nil) + } + + result, err := getVoucherData(ctx, mockSubPrefixDb, "1") + + assert.NoError(t, err) + assert.Equal(t, "SRF", result.Symbol) + assert.Equal(t, "100", result.Balance) + assert.Equal(t, "6", result.Decimal) + assert.Equal(t, "0xd4c288865Ce", result.Address) + + mockSubPrefixDb.AssertExpectations(t) +} + +func TestMatchVoucher(t *testing.T) { + symbols := "1:SRF\n2:MILO" + balances := "1:100\n2:200" + decimals := "1:6\n2:4" + addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa" + + // Test for valid voucher + symbol, balance, decimal, address := matchVoucher("2", symbols, balances, decimals, addresses) + + // Assertions for valid voucher + assert.Equal(t, "MILO", symbol) + assert.Equal(t, "200", balance) + assert.Equal(t, "4", decimal) + assert.Equal(t, "0x41c188d63Qa", address) + + // Test for non-existent voucher + symbol, balance, decimal, address = matchVoucher("3", symbols, balances, decimals, addresses) + + // Assertions for non-match + assert.Equal(t, "", symbol) + assert.Equal(t, "", balance) + assert.Equal(t, "", decimal) + assert.Equal(t, "", address) +} + +func TestStoreTemporaryVoucher(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + ctx := context.Background() + sessionId := "session123" + + voucherData := &VoucherMetadata{ + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + } + + // Define expected entries to be written + expectedEntries := map[utils.DataTyp][]byte{ + utils.DATA_TEMPORARY_SYM: []byte("SRF"), + utils.DATA_TEMPORARY_BAL: []byte("200"), + utils.DATA_TEMPORARY_DECIMAL: []byte("6"), + utils.DATA_TEMPORARY_ADDRESS: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), + } + + // Mock WriteEntry calls + for key, value := range expectedEntries { + mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) + } + + h := &Handlers{userdataStore: mockDataStore} + + err := h.storeTemporaryVoucher(ctx, sessionId, voucherData) + + assert.NoError(t, err) + mockDataStore.AssertExpectations(t) +} + +func TestSetVoucher(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + // Define the temporary voucher data + tempData := &VoucherMetadata{ + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + } + + // Define the expected active entries + activeEntries := map[utils.DataTyp][]byte{ + utils.DATA_ACTIVE_SYM: []byte(tempData.Symbol), + utils.DATA_ACTIVE_BAL: []byte(tempData.Balance), + utils.DATA_ACTIVE_DECIMAL: []byte(tempData.Decimal), + utils.DATA_ACTIVE_ADDRESS: []byte(tempData.Address), + } + + // Define the temporary entries to be cleared + tempEntries := map[utils.DataTyp][]byte{ + utils.DATA_TEMPORARY_SYM: []byte(""), + utils.DATA_TEMPORARY_BAL: []byte(""), + utils.DATA_TEMPORARY_DECIMAL: []byte(""), + utils.DATA_TEMPORARY_ADDRESS: []byte(""), + } + + // Mocking ReadEntry calls for temporary data retrieval + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.Symbol), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.Decimal), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.Address), nil) + + // Mocking WriteEntry calls for setting active data + for key, value := range activeEntries { + mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) + } + + // Mocking WriteEntry calls for clearing temporary data + for key, value := range tempEntries { + mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) + } + + h := &Handlers{ + userdataStore: mockDataStore, + } + + res, err := h.SetVoucher(ctx, "someSym", []byte{}) + + assert.NoError(t, err) + + assert.Equal(t, string(tempData.Symbol), res.Content) + + mockDataStore.AssertExpectations(t) +} + +func TestGetTemporaryVoucherData(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + h := &Handlers{userdataStore: mockDataStore} + + // Mock temporary voucher data + tempData := &VoucherMetadata{ + Symbol: "SRF", + Balance: "100", + Decimal: "6", + Address: "0xd4c288865Ce", + } + + // Set up mocks for reading entries + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.Symbol), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.Decimal), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.Address), nil) + + data, err := h.getTemporaryVoucherData(ctx, sessionId) + assert.NoError(t, err) + assert.Equal(t, tempData, data) + + mockDataStore.AssertExpectations(t) +} + +func TestUpdateVoucherData(t *testing.T) { + mockDataStore := new(mocks.MockUserDataStore) + ctx := context.Background() + sessionId := "session123" + + h := &Handlers{userdataStore: mockDataStore} + + data := &VoucherMetadata{ + Symbol: "SRF", + Balance: "100", + Decimal: "6", + Address: "0xd4c288865Ce", + } + + // Mock WriteEntry for active data + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(data.Symbol)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(data.Balance)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_DECIMAL, []byte(data.Decimal)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_ADDRESS, []byte(data.Address)).Return(nil) + + // Mock WriteEntry for clearing temporary data + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS, []byte("")).Return(nil) + + err := h.updateVoucherData(ctx, sessionId, data) + assert.NoError(t, err) + + mockDataStore.AssertExpectations(t) +} From 0480c02633067fa5638217327fb24b9d1fc62869 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 00:52:23 +0300 Subject: [PATCH 161/175] Moved voucher related functions to the utils package --- internal/handlers/ussd/menuhandler.go | 179 +------------------------ internal/utils/vouchers.go | 184 ++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 172 deletions(-) create mode 100644 internal/utils/vouchers.go diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 98f6c9d..d4ed992 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1118,7 +1118,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - data := processVouchers(vouchersResp.Result.Holdings) + data := utils.ProcessVouchers(vouchersResp.Result.Holdings) // Store all voucher data dataMap := map[string]string{ @@ -1137,31 +1137,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } -// processVouchers converts holdings into formatted strings -func processVouchers(holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` -}) VoucherMetadata { - var data VoucherMetadata - var symbols, balances, decimals, addresses []string - - for i, h := range holdings { - symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) - balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance)) - decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals)) - addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress)) - } - - data.Symbol = strings.Join(symbols, "\n") - data.Balance = strings.Join(balances, "\n") - data.Decimal = strings.Join(decimals, "\n") - data.Address = strings.Join(addresses, "\n") - - return data -} - // GetVoucherList fetches the list of vouchers and formats them func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1193,8 +1168,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - // Retrieve the voucher metadata using the PrefixDb interface - metadata, err := getVoucherData(ctx, h.prefixDb, inputStr) + metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr) if err != nil { return res, fmt.Errorf("failed to retrieve voucher data: %v", err) } @@ -1204,7 +1178,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - if err := h.storeTemporaryVoucher(ctx, sessionId, metadata); err != nil { + if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { return res, err } @@ -1214,86 +1188,9 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } -// getVoucherData retrieves and matches voucher data -func getVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) { - keys := []string{"sym", "bal", "deci", "addr"} - data := make(map[string]string) - - for _, key := range keys { - value, err := db.Get(ctx, []byte(key)) - if err != nil { - return nil, fmt.Errorf("failed to get %s: %v", key, err) - } - data[key] = string(value) - } - - symbol, balance, decimal, address := matchVoucher(input, - data["sym"], - data["bal"], - data["deci"], - data["addr"]) - - if symbol == "" { - return nil, nil - } - - return &VoucherMetadata{ - Symbol: symbol, - Balance: balance, - Decimal: decimal, - Address: address, - }, nil -} - -// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input. -func matchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) { - symList := strings.Split(symbols, "\n") - balList := strings.Split(balances, "\n") - decList := strings.Split(decimals, "\n") - addrList := strings.Split(addresses, "\n") - - for i, sym := range symList { - parts := strings.SplitN(sym, ":", 2) - if len(parts) != 2 { - continue - } - - if input == parts[0] || strings.EqualFold(input, parts[1]) { - symbol = parts[1] - if i < len(balList) { - balance = strings.SplitN(balList[i], ":", 2)[1] - } - if i < len(decList) { - decimal = strings.SplitN(decList[i], ":", 2)[1] - } - if i < len(addrList) { - address = strings.SplitN(addrList[i], ":", 2)[1] - } - break - } - } - return -} - -func (h *Handlers) storeTemporaryVoucher(ctx context.Context, sessionId string, data *VoucherMetadata) error { - entries := map[utils.DataTyp][]byte{ - utils.DATA_TEMPORARY_SYM: []byte(data.Symbol), - utils.DATA_TEMPORARY_BAL: []byte(data.Balance), - utils.DATA_TEMPORARY_DECIMAL: []byte(data.Decimal), - utils.DATA_TEMPORARY_ADDRESS: []byte(data.Address), - } - - for key, value := range entries { - if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil { - return err - } - } - return nil -} - +// SetVoucher retrieves the temp voucher data and sets it as the active data func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -1301,78 +1198,16 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re } // Get temporary data - tempData, err := h.getTemporaryVoucherData(ctx, sessionId) + tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { return res, err } - // Set as active and clear temporary - if err := h.updateVoucherData(ctx, sessionId, tempData); err != nil { + // Set as active and clear temporary data + if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { return res, err } res.Content = tempData.Symbol return res, nil } - -func (h *Handlers) getTemporaryVoucherData(ctx context.Context, sessionId string) (*VoucherMetadata, error) { - store := h.userdataStore - - keys := []utils.DataTyp{ - utils.DATA_TEMPORARY_SYM, - utils.DATA_TEMPORARY_BAL, - utils.DATA_TEMPORARY_DECIMAL, - utils.DATA_TEMPORARY_ADDRESS, - } - - data := &VoucherMetadata{} - values := make([][]byte, len(keys)) - - for i, key := range keys { - value, err := store.ReadEntry(ctx, sessionId, key) - if err != nil { - return nil, err - } - values[i] = value - } - - data.Symbol = string(values[0]) - data.Balance = string(values[1]) - data.Decimal = string(values[2]) - data.Address = string(values[3]) - - return data, nil -} - -func (h *Handlers) updateVoucherData(ctx context.Context, sessionId string, data *VoucherMetadata) error { - // Set active voucher data - activeEntries := map[utils.DataTyp][]byte{ - utils.DATA_ACTIVE_SYM: []byte(data.Symbol), - utils.DATA_ACTIVE_BAL: []byte(data.Balance), - utils.DATA_ACTIVE_DECIMAL: []byte(data.Decimal), - utils.DATA_ACTIVE_ADDRESS: []byte(data.Address), - } - - // Clear temporary voucher data - tempEntries := map[utils.DataTyp][]byte{ - utils.DATA_TEMPORARY_SYM: []byte(""), - utils.DATA_TEMPORARY_BAL: []byte(""), - utils.DATA_TEMPORARY_DECIMAL: []byte(""), - utils.DATA_TEMPORARY_ADDRESS: []byte(""), - } - - // Write all entries - for key, value := range activeEntries { - if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil { - return err - } - } - - for key, value := range tempEntries { - if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil { - return err - } - } - - return nil -} diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go new file mode 100644 index 0000000..11fd7d1 --- /dev/null +++ b/internal/utils/vouchers.go @@ -0,0 +1,184 @@ +package utils + +import ( + "fmt" + "strings" + "context" + + + "git.grassecon.net/urdt/ussd/internal/storage" +) + +// VoucherMetadata helps organize voucher data fields +type VoucherMetadata struct { + Symbol string + Balance string + Decimal string + Address string +} + +// ProcessVouchers converts holdings into formatted strings +func ProcessVouchers(holdings []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` +}) VoucherMetadata { + var data VoucherMetadata + var symbols, balances, decimals, addresses []string + + for i, h := range holdings { + symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) + balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance)) + decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals)) + addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress)) + } + + data.Symbol = strings.Join(symbols, "\n") + data.Balance = strings.Join(balances, "\n") + data.Decimal = strings.Join(decimals, "\n") + data.Address = strings.Join(addresses, "\n") + + return data +} + +// GetVoucherData retrieves and matches voucher data +func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) { + keys := []string{"sym", "bal", "deci", "addr"} + data := make(map[string]string) + + for _, key := range keys { + value, err := db.Get(ctx, []byte(key)) + if err != nil { + return nil, fmt.Errorf("failed to get %s: %v", key, err) + } + data[key] = string(value) + } + + symbol, balance, decimal, address := MatchVoucher(input, + data["sym"], + data["bal"], + data["deci"], + data["addr"]) + + if symbol == "" { + return nil, nil + } + + return &VoucherMetadata{ + Symbol: symbol, + Balance: balance, + Decimal: decimal, + Address: address, + }, nil +} + +// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input. +func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) { + symList := strings.Split(symbols, "\n") + balList := strings.Split(balances, "\n") + decList := strings.Split(decimals, "\n") + addrList := strings.Split(addresses, "\n") + + for i, sym := range symList { + parts := strings.SplitN(sym, ":", 2) + if len(parts) != 2 { + continue + } + + if input == parts[0] || strings.EqualFold(input, parts[1]) { + symbol = parts[1] + if i < len(balList) { + balance = strings.SplitN(balList[i], ":", 2)[1] + } + if i < len(decList) { + decimal = strings.SplitN(decList[i], ":", 2)[1] + } + if i < len(addrList) { + address = strings.SplitN(addrList[i], ":", 2)[1] + } + break + } + } + return +} + +// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore. +func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error { + entries := map[DataTyp][]byte{ + DATA_TEMPORARY_SYM: []byte(data.Symbol), + DATA_TEMPORARY_BAL: []byte(data.Balance), + DATA_TEMPORARY_DECIMAL: []byte(data.Decimal), + DATA_TEMPORARY_ADDRESS: []byte(data.Address), + } + + for key, value := range entries { + if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + return nil +} + +// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore. +func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*VoucherMetadata, error) { + keys := []DataTyp{ + DATA_TEMPORARY_SYM, + DATA_TEMPORARY_BAL, + DATA_TEMPORARY_DECIMAL, + DATA_TEMPORARY_ADDRESS, + } + + data := &VoucherMetadata{} + values := make([][]byte, len(keys)) + + for i, key := range keys { + value, err := store.ReadEntry(ctx, sessionId, key) + if err != nil { + return nil, err + } + values[i] = value + } + + data.Symbol = string(values[0]) + data.Balance = string(values[1]) + data.Decimal = string(values[2]) + data.Address = string(values[3]) + + return data, nil +} + +// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore. +func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error { + // Active voucher data entries + activeEntries := map[DataTyp][]byte{ + DATA_ACTIVE_SYM: []byte(data.Symbol), + DATA_ACTIVE_BAL: []byte(data.Balance), + DATA_ACTIVE_DECIMAL: []byte(data.Decimal), + DATA_ACTIVE_ADDRESS: []byte(data.Address), + } + + // Clear temporary voucher data entries + tempEntries := map[DataTyp][]byte{ + DATA_TEMPORARY_SYM: []byte(""), + DATA_TEMPORARY_BAL: []byte(""), + DATA_TEMPORARY_DECIMAL: []byte(""), + DATA_TEMPORARY_ADDRESS: []byte(""), + } + + // Write active data + for key, value := range activeEntries { + if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + + // Clear temporary data + for key, value := range tempEntries { + if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + + return nil +} From 7241cdbfcbd92155a354c678f1044127c1a15aeb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 00:53:13 +0300 Subject: [PATCH 162/175] Updated the tests --- internal/handlers/ussd/menuhandler_test.go | 169 --------------- internal/utils/vouchers_test.go | 240 +++++++++++++++++++++ 2 files changed, 240 insertions(+), 169 deletions(-) create mode 100644 internal/utils/vouchers_test.go diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index c6ee8cf..6ea44a0 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2050,29 +2050,6 @@ func TestCheckVouchers(t *testing.T) { mockAccountService.AssertExpectations(t) } -func TestProcessVouchers(t *testing.T) { - holdings := []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{ - {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, - {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, - } - - expectedResult := VoucherMetadata{ - Symbol: "1:SRF\n2:MILO", - Balance: "1:100\n2:200", - Decimal: "1:6\n2:4", - Address: "1:0xd4c288865Ce\n2:0x41c188d63Qa", - } - - result := processVouchers(holdings) - - assert.Equal(t, expectedResult, result) -} - func TestGetVoucherList(t *testing.T) { mockSubPrefixDb := new(mocks.MockSubPrefixDb) @@ -2141,92 +2118,6 @@ func TestViewVoucher(t *testing.T) { mockSubPrefixDb.AssertExpectations(t) } -func TestGetVoucherData(t *testing.T) { - mockSubPrefixDb := new(mocks.MockSubPrefixDb) - ctx := context.Background() - - // Mocked voucher data - mockData := map[string][]byte{ - "sym": []byte("1:SRF\n2:MILO"), - "bal": []byte("1:100\n2:200"), - "deci": []byte("1:6\n2:4"), - "addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), - } - - // Mock Get calls - for key, value := range mockData { - mockSubPrefixDb.On("Get", ctx, []byte(key)).Return(value, nil) - } - - result, err := getVoucherData(ctx, mockSubPrefixDb, "1") - - assert.NoError(t, err) - assert.Equal(t, "SRF", result.Symbol) - assert.Equal(t, "100", result.Balance) - assert.Equal(t, "6", result.Decimal) - assert.Equal(t, "0xd4c288865Ce", result.Address) - - mockSubPrefixDb.AssertExpectations(t) -} - -func TestMatchVoucher(t *testing.T) { - symbols := "1:SRF\n2:MILO" - balances := "1:100\n2:200" - decimals := "1:6\n2:4" - addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa" - - // Test for valid voucher - symbol, balance, decimal, address := matchVoucher("2", symbols, balances, decimals, addresses) - - // Assertions for valid voucher - assert.Equal(t, "MILO", symbol) - assert.Equal(t, "200", balance) - assert.Equal(t, "4", decimal) - assert.Equal(t, "0x41c188d63Qa", address) - - // Test for non-existent voucher - symbol, balance, decimal, address = matchVoucher("3", symbols, balances, decimals, addresses) - - // Assertions for non-match - assert.Equal(t, "", symbol) - assert.Equal(t, "", balance) - assert.Equal(t, "", decimal) - assert.Equal(t, "", address) -} - -func TestStoreTemporaryVoucher(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - ctx := context.Background() - sessionId := "session123" - - voucherData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", - } - - // Define expected entries to be written - expectedEntries := map[utils.DataTyp][]byte{ - utils.DATA_TEMPORARY_SYM: []byte("SRF"), - utils.DATA_TEMPORARY_BAL: []byte("200"), - utils.DATA_TEMPORARY_DECIMAL: []byte("6"), - utils.DATA_TEMPORARY_ADDRESS: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), - } - - // Mock WriteEntry calls - for key, value := range expectedEntries { - mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) - } - - h := &Handlers{userdataStore: mockDataStore} - - err := h.storeTemporaryVoucher(ctx, sessionId, voucherData) - - assert.NoError(t, err) - mockDataStore.AssertExpectations(t) -} - func TestSetVoucher(t *testing.T) { mockDataStore := new(mocks.MockUserDataStore) @@ -2285,63 +2176,3 @@ func TestSetVoucher(t *testing.T) { mockDataStore.AssertExpectations(t) } - -func TestGetTemporaryVoucherData(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - h := &Handlers{userdataStore: mockDataStore} - - // Mock temporary voucher data - tempData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "100", - Decimal: "6", - Address: "0xd4c288865Ce", - } - - // Set up mocks for reading entries - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.Symbol), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.Decimal), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.Address), nil) - - data, err := h.getTemporaryVoucherData(ctx, sessionId) - assert.NoError(t, err) - assert.Equal(t, tempData, data) - - mockDataStore.AssertExpectations(t) -} - -func TestUpdateVoucherData(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - ctx := context.Background() - sessionId := "session123" - - h := &Handlers{userdataStore: mockDataStore} - - data := &VoucherMetadata{ - Symbol: "SRF", - Balance: "100", - Decimal: "6", - Address: "0xd4c288865Ce", - } - - // Mock WriteEntry for active data - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(data.Symbol)).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(data.Balance)).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_DECIMAL, []byte(data.Decimal)).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_ADDRESS, []byte(data.Address)).Return(nil) - - // Mock WriteEntry for clearing temporary data - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS, []byte("")).Return(nil) - - err := h.updateVoucherData(ctx, sessionId, data) - assert.NoError(t, err) - - mockDataStore.AssertExpectations(t) -} diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go new file mode 100644 index 0000000..75ae563 --- /dev/null +++ b/internal/utils/vouchers_test.go @@ -0,0 +1,240 @@ +package utils + +import ( + "context" + "testing" + + "git.grassecon.net/urdt/ussd/internal/storage" + "github.com/alecthomas/assert/v2" + "github.com/stretchr/testify/require" + + memdb "git.defalsify.org/vise.git/db/mem" +) + +// AssertEmptyValue checks if a value is empty/nil/zero +func AssertEmptyValue(t *testing.T, value []byte, msgAndArgs ...interface{}) { + assert.Equal(t, len(value), 0, msgAndArgs...) +} + +func TestMatchVoucher(t *testing.T) { + symbols := "1:SRF\n2:MILO" + balances := "1:100\n2:200" + decimals := "1:6\n2:4" + addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa" + + // Test for valid voucher + symbol, balance, decimal, address := MatchVoucher("2", symbols, balances, decimals, addresses) + + // Assertions for valid voucher + assert.Equal(t, "MILO", symbol) + assert.Equal(t, "200", balance) + assert.Equal(t, "4", decimal) + assert.Equal(t, "0x41c188d63Qa", address) + + // Test for non-existent voucher + symbol, balance, decimal, address = MatchVoucher("3", symbols, balances, decimals, addresses) + + // Assertions for non-match + assert.Equal(t, "", symbol) + assert.Equal(t, "", balance) + assert.Equal(t, "", decimal) + assert.Equal(t, "", address) +} + +func TestProcessVouchers(t *testing.T) { + holdings := []struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` + }{ + {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, + {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, + } + + expectedResult := VoucherMetadata{ + Symbol: "1:SRF\n2:MILO", + Balance: "1:100\n2:200", + Decimal: "1:6\n2:4", + Address: "1:0xd4c288865Ce\n2:0x41c188d63Qa", + } + + result := ProcessVouchers(holdings) + + assert.Equal(t, expectedResult, result) +} + +func TestGetVoucherData(t *testing.T) { + ctx := context.Background() + + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + + // Test voucher data + mockData := map[string][]byte{ + "sym": []byte("1:SRF\n2:MILO"), + "bal": []byte("1:100\n2:200"), + "deci": []byte("1:6\n2:4"), + "addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), + } + + // Put the data + for key, value := range mockData { + err = spdb.Put(ctx, []byte(key), []byte(value)) + if err != nil { + t.Fatal(err) + } + } + + result, err := GetVoucherData(ctx, spdb, "1") + + assert.NoError(t, err) + assert.Equal(t, "SRF", result.Symbol) + assert.Equal(t, "100", result.Balance) + assert.Equal(t, "6", result.Decimal) + assert.Equal(t, "0xd4c288865Ce", result.Address) +} + +func TestStoreTemporaryVoucher(t *testing.T) { + ctx := context.Background() + sessionId := "session123" + + // Initialize memDb + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + require.NoError(t, err) + defer db.Close() + + // Create UserDataStore with memDb + store := &UserDataStore{ + Db: db, + } + + // Test data + voucherData := &VoucherMetadata{ + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + } + + // Execute the function being tested + err = StoreTemporaryVoucher(ctx, store, sessionId, voucherData) + assert.NoError(t, err) + + // Verify stored data + expectedEntries := map[DataTyp][]byte{ + DATA_TEMPORARY_SYM: []byte("SRF"), + DATA_TEMPORARY_BAL: []byte("200"), + DATA_TEMPORARY_DECIMAL: []byte("6"), + DATA_TEMPORARY_ADDRESS: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), + } + + for key, expectedValue := range expectedEntries { + storedValue, err := store.ReadEntry(ctx, sessionId, key) + assert.NoError(t, err) + assert.Equal(t, expectedValue, storedValue, "Mismatch for key %v", key) + } +} + +func TestGetTemporaryVoucherData(t *testing.T) { + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + // Initialize memDb + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + require.NoError(t, err) + defer db.Close() + + // Create UserDataStore with memDb + store := &UserDataStore{ + Db: db, + } + + // Test voucher data + tempData := &VoucherMetadata{ + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + } + + // store the data + err = StoreTemporaryVoucher(ctx, store, sessionId, tempData) + assert.NoError(t, err) + + // Execute the function being tested + data, err := GetTemporaryVoucherData(ctx, store, sessionId) + assert.NoError(t, err) + assert.Equal(t, tempData, data) +} + +func TestUpdateVoucherData(t *testing.T) { + sessionId := "session123" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + // Initialize memDb + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + require.NoError(t, err) + defer db.Close() + + store := &UserDataStore{ + Db: db, + } + + // Test data + data := &VoucherMetadata{ + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + } + + // First store some temporary data to verify it gets cleared + tempData := &VoucherMetadata{ + Symbol: "OLD", + Balance: "100", + Decimal: "8", + Address: "0xold", + } + err = StoreTemporaryVoucher(ctx, store, sessionId, tempData) + require.NoError(t, err) + + // Execute update + err = UpdateVoucherData(ctx, store, sessionId, data) + assert.NoError(t, err) + + // Verify active data was stored correctly + activeEntries := map[DataTyp][]byte{ + DATA_ACTIVE_SYM: []byte(data.Symbol), + DATA_ACTIVE_BAL: []byte(data.Balance), + DATA_ACTIVE_DECIMAL: []byte(data.Decimal), + DATA_ACTIVE_ADDRESS: []byte(data.Address), + } + + for key, expectedValue := range activeEntries { + storedValue, err := store.ReadEntry(ctx, sessionId, key) + assert.NoError(t, err) + assert.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key) + } + + // Verify temporary data was cleared + tempKeys := []DataTyp{ + DATA_TEMPORARY_SYM, + DATA_TEMPORARY_BAL, + DATA_TEMPORARY_DECIMAL, + DATA_TEMPORARY_ADDRESS, + } + + for _, key := range tempKeys { + storedValue, err := store.ReadEntry(ctx, sessionId, key) + assert.NoError(t, err) + AssertEmptyValue(t, storedValue, "Temporary data not cleared for key %v", key) + } +} \ No newline at end of file From cb997159f785e796f9d2bdb1ab7f58cae46d99f7 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 00:59:59 +0000 Subject: [PATCH 163/175] Add reverse sessionid address lookup --- common/hex.go | 18 ++++++++++++++++++ internal/handlers/ussd/menuhandler.go | 13 +++++++++++-- internal/handlers/ussd/menuhandler_test.go | 12 ++++++++++-- internal/utils/db.go | 1 + 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 common/hex.go diff --git a/common/hex.go b/common/hex.go new file mode 100644 index 0000000..f5aa7ed --- /dev/null +++ b/common/hex.go @@ -0,0 +1,18 @@ +package common + +import ( + "encoding/hex" +) + +func NormalizeHex(s string) (string, error) { + if len(s) >= 2 { + if s[:2] == "0x" { + s = s[2:] + } + } + r, err := hex.DecodeString(s) + if err != nil { + return "", err + } + return hex.EncodeToString(r), nil +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 7bc8250..dae4236 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -21,6 +21,7 @@ import ( "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/common" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -154,13 +155,21 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r utils.DATA_TRACKING_ID: trackingId, utils.DATA_PUBLIC_KEY: publicKey, } + store := h.userdataStore for key, value := range data { - store := h.userdataStore - err := store.WriteEntry(ctx, sessionId, key, []byte(value)) + err = store.WriteEntry(ctx, sessionId, key, []byte(value)) if err != nil { return err } } + publicKeyNormalized, err := common.NormalizeHex(publicKey) + if err != nil { + return err + } + err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) + if err != nil { + return err + } res.FlagSet = append(res.FlagSet, flag_account_created) return nil diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 28d25e8..0b70e80 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -20,6 +20,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/common" "github.com/alecthomas/assert/v2" "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" @@ -96,7 +97,7 @@ func TestCreateAccount(t *testing.T) { Description: "Account creation successed", Result: map[string]any{ "trackingId": "1234567890", - "publicKey": "1235QERYU", + "publicKey": "0xD3adB33f", }, }, expectedResult: resource.Result{ @@ -117,9 +118,10 @@ func TestCreateAccount(t *testing.T) { flagManager: fm.parser, } + publicKey := tt.serverResponse.Result["publicKey"].(string) data := map[utils.DataTyp]string{ utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), - utils.DATA_PUBLIC_KEY: tt.serverResponse.Result["publicKey"].(string), + utils.DATA_PUBLIC_KEY: publicKey, } mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) @@ -128,6 +130,12 @@ func TestCreateAccount(t *testing.T) { for key, value := range data { mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil) } + publicKeyNormalized, err := common.NormalizeHex(publicKey) + if err != nil { + t.Fatal(err) + } + + mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)).Return(nil) // Call the method you want to test res, err := h.CreateAccount(ctx, "create_account", []byte("some-input")) diff --git a/internal/utils/db.go b/internal/utils/db.go index 45e7681..2c1e6fa 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -28,6 +28,7 @@ const ( DATA_ACTIVE_SYM DATA_TEMPORARY_BAL DATA_ACTIVE_BAL + DATA_PUBLIC_KEY_REVERSE ) func typToBytes(typ DataTyp) []byte { From adaa0c63ef78486223ec5e9646640599ceda685d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 14:12:42 +0300 Subject: [PATCH 164/175] Extract common db operations for the test --- internal/utils/vouchers_test.go | 138 ++++++++++++++------------------ 1 file changed, 62 insertions(+), 76 deletions(-) diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go index 75ae563..f26ee01 100644 --- a/internal/utils/vouchers_test.go +++ b/internal/utils/vouchers_test.go @@ -11,6 +11,25 @@ import ( memdb "git.defalsify.org/vise.git/db/mem" ) +// InitializeTestDb sets up and returns an in-memory database and store. +func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) { + ctx := context.Background() + + // Initialize memDb + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + require.NoError(t, err, "Failed to connect to memDb") + + // Create UserDataStore with memDb + store := &UserDataStore{Db: db} + + t.Cleanup(func() { + db.Close() // Ensure the DB is closed after each test + }) + + return ctx, store +} + // AssertEmptyValue checks if a value is empty/nil/zero func AssertEmptyValue(t *testing.T, value []byte, msgAndArgs ...interface{}) { assert.Equal(t, len(value), 0, msgAndArgs...) @@ -100,31 +119,20 @@ func TestGetVoucherData(t *testing.T) { } func TestStoreTemporaryVoucher(t *testing.T) { - ctx := context.Background() + ctx, store := InitializeTestDb(t) sessionId := "session123" - - // Initialize memDb - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - require.NoError(t, err) - defer db.Close() - - // Create UserDataStore with memDb - store := &UserDataStore{ - Db: db, - } // Test data voucherData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } // Execute the function being tested - err = StoreTemporaryVoucher(ctx, store, sessionId, voucherData) - assert.NoError(t, err) + err := StoreTemporaryVoucher(ctx, store, sessionId, voucherData) + require.NoError(t, err) // Verify stored data expectedEntries := map[DataTyp][]byte{ @@ -136,92 +144,70 @@ func TestStoreTemporaryVoucher(t *testing.T) { for key, expectedValue := range expectedEntries { storedValue, err := store.ReadEntry(ctx, sessionId, key) - assert.NoError(t, err) - assert.Equal(t, expectedValue, storedValue, "Mismatch for key %v", key) + require.NoError(t, err) + require.Equal(t, expectedValue, storedValue, "Mismatch for key %v", key) } } func TestGetTemporaryVoucherData(t *testing.T) { + ctx, store := InitializeTestDb(t) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Initialize memDb - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - require.NoError(t, err) - defer db.Close() - - // Create UserDataStore with memDb - store := &UserDataStore{ - Db: db, - } // Test voucher data tempData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } - // store the data - err = StoreTemporaryVoucher(ctx, store, sessionId, tempData) - assert.NoError(t, err) - + // Store the data + err := StoreTemporaryVoucher(ctx, store, sessionId, tempData) + require.NoError(t, err) + // Execute the function being tested data, err := GetTemporaryVoucherData(ctx, store, sessionId) - assert.NoError(t, err) - assert.Equal(t, tempData, data) + require.NoError(t, err) + require.Equal(t, tempData, data) } func TestUpdateVoucherData(t *testing.T) { + ctx, store := InitializeTestDb(t) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Initialize memDb - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - require.NoError(t, err) - defer db.Close() - - store := &UserDataStore{ - Db: db, + // New voucher data + newData := &VoucherMetadata{ + Symbol: "SRF", + Balance: "200", + Decimal: "6", + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } - // Test data - data := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", - } - - // First store some temporary data to verify it gets cleared + // Old temporary data tempData := &VoucherMetadata{ - Symbol: "OLD", - Balance: "100", - Decimal: "8", - Address: "0xold", + Symbol: "OLD", + Balance: "100", + Decimal: "8", + Address: "0xold", } - err = StoreTemporaryVoucher(ctx, store, sessionId, tempData) - require.NoError(t, err) + require.NoError(t, StoreTemporaryVoucher(ctx, store, sessionId, tempData)) // Execute update - err = UpdateVoucherData(ctx, store, sessionId, data) - assert.NoError(t, err) + err := UpdateVoucherData(ctx, store, sessionId, newData) + require.NoError(t, err) // Verify active data was stored correctly activeEntries := map[DataTyp][]byte{ - DATA_ACTIVE_SYM: []byte(data.Symbol), - DATA_ACTIVE_BAL: []byte(data.Balance), - DATA_ACTIVE_DECIMAL: []byte(data.Decimal), - DATA_ACTIVE_ADDRESS: []byte(data.Address), + DATA_ACTIVE_SYM: []byte(newData.Symbol), + DATA_ACTIVE_BAL: []byte(newData.Balance), + DATA_ACTIVE_DECIMAL: []byte(newData.Decimal), + DATA_ACTIVE_ADDRESS: []byte(newData.Address), } for key, expectedValue := range activeEntries { storedValue, err := store.ReadEntry(ctx, sessionId, key) - assert.NoError(t, err) - assert.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key) + require.NoError(t, err) + require.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key) } // Verify temporary data was cleared @@ -234,7 +220,7 @@ func TestUpdateVoucherData(t *testing.T) { for _, key := range tempKeys { storedValue, err := store.ReadEntry(ctx, sessionId, key) - assert.NoError(t, err) + require.NoError(t, err) AssertEmptyValue(t, storedValue, "Temporary data not cleared for key %v", key) } -} \ No newline at end of file +} From 66b34eaea4f83147e6f4e3296636eee0bf21e812 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 18:21:49 +0300 Subject: [PATCH 165/175] get the ussd-data-service package --- go.mod | 2 ++ go.sum | 2 ++ 2 files changed, 4 insertions(+) diff --git a/go.mod b/go.mod index 0b30354..391c1a5 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ require ( gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) +require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect + require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect diff --git a/go.sum b/go.sum index d566e2c..0ba38c1 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU= github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo= +github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a h1:q/YH7nE2j8epNmFnTu0tU1vwtCxtQ6nH+d7hRVV5krU= +github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a/go.mod h1:hdKaKwqiW6/kphK4j/BhmuRlZDLo1+DYo3gYw5O0siw= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= From caafe495be8d60a04e36d4306d4c2509adce8a22 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 18:30:55 +0300 Subject: [PATCH 166/175] use the dataserviceapi structs --- internal/handlers/ussd/menuhandler.go | 8 +-- internal/handlers/ussd/menuhandler_test.go | 43 +++---------- internal/models/vouchersresponse.go | 21 ++++--- .../testservice/TestAccountService.go | 17 +----- internal/utils/vouchers.go | 49 +++++++-------- internal/utils/vouchers_test.go | 60 +++++++++---------- 6 files changed, 74 insertions(+), 124 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d4ed992..231da95 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1055,13 +1055,13 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by if db.IsNotFound(err) { publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { - return res, nil + return res, err } // Fetch vouchers from the API using the public key vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) if err != nil { - return res, nil + return res, err } // Return if there is no voucher @@ -1183,7 +1183,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) - res.Content = fmt.Sprintf("%s\n%s", metadata.Symbol, metadata.Balance) + res.Content = fmt.Sprintf("%s\n%s", metadata.TokenSymbol, metadata.Balance) return res, nil } @@ -1208,6 +1208,6 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re return res, err } - res.Content = tempData.Symbol + res.Content = tempData.TokenSymbol return res, nil } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6ea44a0..2b76bf1 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -24,6 +24,8 @@ import ( "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" + + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) var ( @@ -1898,12 +1900,10 @@ func TestSetDefaultVoucher(t *testing.T) { if err != nil { t.Logf(err.Error()) } - flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher") if err != nil { t.Logf(err.Error()) } - // Define session ID and mock data sessionId := "session123" publicKey := "0X13242618721" @@ -1920,20 +1920,8 @@ func TestSetDefaultVoucher(t *testing.T) { vouchersResp: &models.VoucherHoldingResponse{ Ok: true, Description: "Vouchers fetched successfully", - Result: struct { - Holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - } `json:"holdings"` - }{ - Holdings: []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{ + Result: models.VoucherResult{ + Holdings: []dataserviceapi.TokenHoldings{ { ContractAddress: "0x123", TokenSymbol: "TOKEN1", @@ -1950,20 +1938,8 @@ func TestSetDefaultVoucher(t *testing.T) { vouchersResp: &models.VoucherHoldingResponse{ Ok: true, Description: "No vouchers available", - Result: struct { - Holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - } `json:"holdings"` - }{ - Holdings: []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{}, + Result: models.VoucherResult{ + Holdings: []dataserviceapi.TokenHoldings{}, }, }, expectedResult: resource.Result{ @@ -2025,12 +2001,7 @@ func TestCheckVouchers(t *testing.T) { mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) mockVouchersResponse := &models.VoucherHoldingResponse{} - mockVouchersResponse.Result.Holdings = []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{ + mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{ {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go index 010730f..09b085d 100644 --- a/internal/models/vouchersresponse.go +++ b/internal/models/vouchersresponse.go @@ -1,15 +1,14 @@ package models -// VoucherHoldingResponse represents a single voucher holding +import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" + type VoucherHoldingResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` - Result struct { - Holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - } `json:"holdings"` - } `json:"result"` + Ok bool `json:"ok"` + Description string `json:"description"` + Result VoucherResult `json:"result"` +} + +// VoucherResult holds the list of token holdings +type VoucherResult struct { + Holdings []dataserviceapi.TokenHoldings `json:"holdings"` } diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 6332345..745b80d 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -7,6 +7,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/models" "github.com/grassrootseconomics/eth-custodial/pkg/api" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) type TestAccountService struct { @@ -75,20 +76,8 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { return &models.VoucherHoldingResponse{ Ok: true, - Result: struct { - Holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - } `json:"holdings"` - }{ - Holdings: []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{ + Result: models.VoucherResult{ + Holdings: []dataserviceapi.TokenHoldings{ { ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", TokenSymbol: "SRF", diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go index 11fd7d1..2aed42a 100644 --- a/internal/utils/vouchers.go +++ b/internal/utils/vouchers.go @@ -1,12 +1,12 @@ package utils import ( + "context" "fmt" "strings" - "context" - "git.grassecon.net/urdt/ussd/internal/storage" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) // VoucherMetadata helps organize voucher data fields @@ -18,12 +18,7 @@ type VoucherMetadata struct { } // ProcessVouchers converts holdings into formatted strings -func ProcessVouchers(holdings []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` -}) VoucherMetadata { +func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { var data VoucherMetadata var symbols, balances, decimals, addresses []string @@ -43,7 +38,7 @@ func ProcessVouchers(holdings []struct { } // GetVoucherData retrieves and matches voucher data -func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) { +func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { keys := []string{"sym", "bal", "deci", "addr"} data := make(map[string]string) @@ -65,11 +60,11 @@ func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*Vo return nil, nil } - return &VoucherMetadata{ - Symbol: symbol, - Balance: balance, - Decimal: decimal, - Address: address, + return &dataserviceapi.TokenHoldings{ + TokenSymbol: string(symbol), + Balance: string(balance), + TokenDecimals: string(decimal), + ContractAddress: string(address), }, nil } @@ -104,12 +99,12 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, } // StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore. -func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error { +func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { entries := map[DataTyp][]byte{ - DATA_TEMPORARY_SYM: []byte(data.Symbol), + DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), DATA_TEMPORARY_BAL: []byte(data.Balance), - DATA_TEMPORARY_DECIMAL: []byte(data.Decimal), - DATA_TEMPORARY_ADDRESS: []byte(data.Address), + DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), + DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), } for key, value := range entries { @@ -121,7 +116,7 @@ func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId strin } // GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore. -func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*VoucherMetadata, error) { +func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) { keys := []DataTyp{ DATA_TEMPORARY_SYM, DATA_TEMPORARY_BAL, @@ -129,7 +124,7 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str DATA_TEMPORARY_ADDRESS, } - data := &VoucherMetadata{} + data := &dataserviceapi.TokenHoldings{} values := make([][]byte, len(keys)) for i, key := range keys { @@ -140,22 +135,22 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str values[i] = value } - data.Symbol = string(values[0]) + data.TokenSymbol = string(values[0]) data.Balance = string(values[1]) - data.Decimal = string(values[2]) - data.Address = string(values[3]) + data.TokenDecimals = string(values[2]) + data.ContractAddress = string(values[3]) return data, nil } // UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore. -func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error { +func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { // Active voucher data entries activeEntries := map[DataTyp][]byte{ - DATA_ACTIVE_SYM: []byte(data.Symbol), + DATA_ACTIVE_SYM: []byte(data.TokenSymbol), DATA_ACTIVE_BAL: []byte(data.Balance), - DATA_ACTIVE_DECIMAL: []byte(data.Decimal), - DATA_ACTIVE_ADDRESS: []byte(data.Address), + DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals), + DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress), } // Clear temporary voucher data entries diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go index f26ee01..8f8f18e 100644 --- a/internal/utils/vouchers_test.go +++ b/internal/utils/vouchers_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" memdb "git.defalsify.org/vise.git/db/mem" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) // InitializeTestDb sets up and returns an in-memory database and store. @@ -61,12 +62,7 @@ func TestMatchVoucher(t *testing.T) { } func TestProcessVouchers(t *testing.T) { - holdings := []struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` - }{ + holdings := []dataserviceapi.TokenHoldings{ {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } @@ -112,10 +108,10 @@ func TestGetVoucherData(t *testing.T) { result, err := GetVoucherData(ctx, spdb, "1") assert.NoError(t, err) - assert.Equal(t, "SRF", result.Symbol) + assert.Equal(t, "SRF", result.TokenSymbol) assert.Equal(t, "100", result.Balance) - assert.Equal(t, "6", result.Decimal) - assert.Equal(t, "0xd4c288865Ce", result.Address) + assert.Equal(t, "6", result.TokenDecimals) + assert.Equal(t, "0xd4c288865Ce", result.ContractAddress) } func TestStoreTemporaryVoucher(t *testing.T) { @@ -123,11 +119,11 @@ func TestStoreTemporaryVoucher(t *testing.T) { sessionId := "session123" // Test data - voucherData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + voucherData := &dataserviceapi.TokenHoldings{ + TokenSymbol: "SRF", + Balance: "200", + TokenDecimals: "6", + ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } // Execute the function being tested @@ -154,11 +150,11 @@ func TestGetTemporaryVoucherData(t *testing.T) { sessionId := "session123" // Test voucher data - tempData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + tempData := &dataserviceapi.TokenHoldings{ + TokenSymbol: "SRF", + Balance: "200", + TokenDecimals: "6", + ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } // Store the data @@ -176,19 +172,19 @@ func TestUpdateVoucherData(t *testing.T) { sessionId := "session123" // New voucher data - newData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + newData := &dataserviceapi.TokenHoldings{ + TokenSymbol: "SRF", + Balance: "200", + TokenDecimals: "6", + ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } // Old temporary data - tempData := &VoucherMetadata{ - Symbol: "OLD", - Balance: "100", - Decimal: "8", - Address: "0xold", + tempData := &dataserviceapi.TokenHoldings{ + TokenSymbol: "OLD", + Balance: "100", + TokenDecimals: "8", + ContractAddress: "0xold", } require.NoError(t, StoreTemporaryVoucher(ctx, store, sessionId, tempData)) @@ -198,10 +194,10 @@ func TestUpdateVoucherData(t *testing.T) { // Verify active data was stored correctly activeEntries := map[DataTyp][]byte{ - DATA_ACTIVE_SYM: []byte(newData.Symbol), + DATA_ACTIVE_SYM: []byte(newData.TokenSymbol), DATA_ACTIVE_BAL: []byte(newData.Balance), - DATA_ACTIVE_DECIMAL: []byte(newData.Decimal), - DATA_ACTIVE_ADDRESS: []byte(newData.Address), + DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals), + DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress), } for key, expectedValue := range activeEntries { From 8b399781e893ab6b678b0d38b7b960f0b60a9a4a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 18:40:03 +0300 Subject: [PATCH 167/175] make the VoucherMetadata more descriptive --- internal/handlers/ussd/menuhandler.go | 16 ++++----------- internal/handlers/ussd/menuhandler_test.go | 24 +++++++++++----------- internal/utils/vouchers.go | 18 ++++++++-------- internal/utils/vouchers_test.go | 8 ++++---- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 231da95..3de4590 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -58,14 +58,6 @@ func (fm *FlagManager) GetFlag(label string) (uint32, error) { return fm.parser.GetFlag(label) } -// VoucherMetadata helps organize voucher data fields -type VoucherMetadata struct { - Symbol string - Balance string - Decimal string - Address string -} - type Handlers struct { pe *persist.Persister st *state.State @@ -1122,10 +1114,10 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) // Store all voucher data dataMap := map[string]string{ - "sym": data.Symbol, - "bal": data.Balance, - "deci": data.Decimal, - "addr": data.Address, + "sym": data.Symbols, + "bal": data.Balances, + "deci": data.Decimals, + "addr": data.Addresses, } for key, value := range dataMap { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 2b76bf1..12f155e 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2096,19 +2096,19 @@ func TestSetVoucher(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Define the temporary voucher data - tempData := &VoucherMetadata{ - Symbol: "SRF", - Balance: "200", - Decimal: "6", - Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + tempData := &dataserviceapi.TokenHoldings{ + TokenSymbol: "SRF", + Balance: "200", + TokenDecimals: "6", + ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } // Define the expected active entries activeEntries := map[utils.DataTyp][]byte{ - utils.DATA_ACTIVE_SYM: []byte(tempData.Symbol), + utils.DATA_ACTIVE_SYM: []byte(tempData.TokenSymbol), utils.DATA_ACTIVE_BAL: []byte(tempData.Balance), - utils.DATA_ACTIVE_DECIMAL: []byte(tempData.Decimal), - utils.DATA_ACTIVE_ADDRESS: []byte(tempData.Address), + utils.DATA_ACTIVE_DECIMAL: []byte(tempData.TokenDecimals), + utils.DATA_ACTIVE_ADDRESS: []byte(tempData.ContractAddress), } // Define the temporary entries to be cleared @@ -2120,10 +2120,10 @@ func TestSetVoucher(t *testing.T) { } // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.Symbol), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.TokenSymbol), nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.Decimal), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.Address), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.TokenDecimals), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.ContractAddress), nil) // Mocking WriteEntry calls for setting active data for key, value := range activeEntries { @@ -2143,7 +2143,7 @@ func TestSetVoucher(t *testing.T) { assert.NoError(t, err) - assert.Equal(t, string(tempData.Symbol), res.Content) + assert.Equal(t, string(tempData.TokenSymbol), res.Content) mockDataStore.AssertExpectations(t) } diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go index 2aed42a..73a95a6 100644 --- a/internal/utils/vouchers.go +++ b/internal/utils/vouchers.go @@ -9,12 +9,12 @@ import ( dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) -// VoucherMetadata helps organize voucher data fields +// VoucherMetadata helps organize data fields type VoucherMetadata struct { - Symbol string - Balance string - Decimal string - Address string + Symbols string + Balances string + Decimals string + Addresses string } // ProcessVouchers converts holdings into formatted strings @@ -29,10 +29,10 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress)) } - data.Symbol = strings.Join(symbols, "\n") - data.Balance = strings.Join(balances, "\n") - data.Decimal = strings.Join(decimals, "\n") - data.Address = strings.Join(addresses, "\n") + data.Symbols = strings.Join(symbols, "\n") + data.Balances = strings.Join(balances, "\n") + data.Decimals = strings.Join(decimals, "\n") + data.Addresses = strings.Join(addresses, "\n") return data } diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go index 8f8f18e..953182e 100644 --- a/internal/utils/vouchers_test.go +++ b/internal/utils/vouchers_test.go @@ -68,10 +68,10 @@ func TestProcessVouchers(t *testing.T) { } expectedResult := VoucherMetadata{ - Symbol: "1:SRF\n2:MILO", - Balance: "1:100\n2:200", - Decimal: "1:6\n2:4", - Address: "1:0xd4c288865Ce\n2:0x41c188d63Qa", + Symbols: "1:SRF\n2:MILO", + Balances: "1:100\n2:200", + Decimals: "1:6\n2:4", + Addresses: "1:0xd4c288865Ce\n2:0x41c188d63Qa", } result := ProcessVouchers(holdings) From 05ed236e03b48de4500fd3fd482ad42c60aa65ae Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 14:14:10 +0300 Subject: [PATCH 168/175] add node to update individual profile information --- services/registration/update_age | 2 ++ services/registration/update_familyname.vis | 2 ++ services/registration/update_firstname.vis | 2 ++ services/registration/update_gender.vis | 2 ++ services/registration/update_location.vis | 2 ++ services/registration/update_offerings.vis | 2 ++ services/registration/update_yob.vis | 2 ++ 7 files changed, 14 insertions(+) create mode 100644 services/registration/update_age create mode 100644 services/registration/update_familyname.vis create mode 100644 services/registration/update_firstname.vis create mode 100644 services/registration/update_gender.vis create mode 100644 services/registration/update_location.vis create mode 100644 services/registration/update_offerings.vis create mode 100644 services/registration/update_yob.vis diff --git a/services/registration/update_age b/services/registration/update_age new file mode 100644 index 0000000..76ca306 --- /dev/null +++ b/services/registration/update_age @@ -0,0 +1,2 @@ +RELOAD save_yob +CATCH profile_update_success flag_allow_update 1 \ No newline at end of file diff --git a/services/registration/update_familyname.vis b/services/registration/update_familyname.vis new file mode 100644 index 0000000..7cd4d9f --- /dev/null +++ b/services/registration/update_familyname.vis @@ -0,0 +1,2 @@ +RELOAD save_familyname +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_firstname.vis b/services/registration/update_firstname.vis new file mode 100644 index 0000000..dca7036 --- /dev/null +++ b/services/registration/update_firstname.vis @@ -0,0 +1,2 @@ +RELOAD save_firstname +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_gender.vis b/services/registration/update_gender.vis new file mode 100644 index 0000000..506a56a --- /dev/null +++ b/services/registration/update_gender.vis @@ -0,0 +1,2 @@ +RELOAD save_gender +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_location.vis b/services/registration/update_location.vis new file mode 100644 index 0000000..16c4ea2 --- /dev/null +++ b/services/registration/update_location.vis @@ -0,0 +1,2 @@ +RELOAD save_location +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_offerings.vis b/services/registration/update_offerings.vis new file mode 100644 index 0000000..4aeed74 --- /dev/null +++ b/services/registration/update_offerings.vis @@ -0,0 +1,2 @@ +RELOAD save_offerings +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_yob.vis b/services/registration/update_yob.vis new file mode 100644 index 0000000..a9388ae --- /dev/null +++ b/services/registration/update_yob.vis @@ -0,0 +1,2 @@ +RELOAD save_yob +CATCH profile_update_success flag_allow_update 1 From 981f7ca4f6a68d55429ce8efc99c0aee53d217d8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 14:14:50 +0300 Subject: [PATCH 169/175] add keys to for holding temporary profile info --- internal/utils/db.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/utils/db.go b/internal/utils/db.go index 2c1e6fa..e8e7fbc 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -14,11 +14,17 @@ const ( DATA_CUSTODIAL_ID DATA_ACCOUNT_PIN DATA_ACCOUNT_STATUS + DATA_TEMPORARY_FIRST_NAME DATA_FIRST_NAME + DATA_TEMPORARY_FAMILY_NAME DATA_FAMILY_NAME + DATA_TEMPORARY_YOB DATA_YOB + DATA_TEMPORARY_LOCATION DATA_LOCATION + DATA_TEMPORARY_GENDER DATA_GENDER + DATA_TEMPORARY_OFFERINGS DATA_OFFERINGS DATA_RECIPIENT DATA_AMOUNT @@ -29,6 +35,7 @@ const ( DATA_TEMPORARY_BAL DATA_ACTIVE_BAL DATA_PUBLIC_KEY_REVERSE + ) func typToBytes(typ DataTyp) []byte { From 211cc1f7751b64b9c70e52d80697ec6c056def10 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 14:19:12 +0300 Subject: [PATCH 170/175] perform profile update in individual update node --- services/registration/set_female.vis | 2 +- services/registration/set_male.vis | 2 +- services/registration/set_unspecified.vis | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/registration/set_female.vis b/services/registration/set_female.vis index 723b080..e211ada 100644 --- a/services/registration/set_female.vis +++ b/services/registration/set_female.vis @@ -1,4 +1,4 @@ LOAD save_gender 0 CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_gender flag_allow_update 1 MOVE pin_entry diff --git a/services/registration/set_male.vis b/services/registration/set_male.vis index 723b080..e211ada 100644 --- a/services/registration/set_male.vis +++ b/services/registration/set_male.vis @@ -1,4 +1,4 @@ LOAD save_gender 0 CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_gender flag_allow_update 1 MOVE pin_entry diff --git a/services/registration/set_unspecified.vis b/services/registration/set_unspecified.vis index 723b080..e211ada 100644 --- a/services/registration/set_unspecified.vis +++ b/services/registration/set_unspecified.vis @@ -1,4 +1,4 @@ LOAD save_gender 0 CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_gender flag_allow_update 1 MOVE pin_entry From c45fcda2f16e30df9297e0604905ae40839b17f1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 14:20:04 +0300 Subject: [PATCH 171/175] remove back option check,protect profile data update --- internal/handlers/ussd/menuhandler.go | 118 +++++++++++++++++--------- 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dae4236..36d8a4a 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -19,9 +19,9 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" + "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" - "git.grassecon.net/urdt/ussd/common" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -33,7 +33,6 @@ var ( translationDir = path.Join(scriptDir, "locale") okResponse *api.OKResponse errResponse *api.ErrResponse - backOption = []byte("0") ) // FlagManager handles centralized flag management @@ -329,13 +328,18 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - if bytes.Equal(input, backOption) { - return res, nil + firstName := string(input) + store := h.userdataStore + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + if allowUpdate { + temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME) + err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) + if err != nil { + return res, err } - firstName := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)) + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)) if err != nil { return res, err } @@ -352,20 +356,24 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - if bytes.Equal(input, backOption) { - return res, nil - } - familyName := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)) + store := h.userdataStore + familyName := string(input) + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME) + err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { return res, err } } else { - return res, fmt.Errorf("a family name cannot be less than one character") + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)) + if err != nil { + return res, err + } } - return res, nil } @@ -377,10 +385,19 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou if !ok { return res, fmt.Errorf("missing session") } - if len(input) == 4 { - yob := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob)) + yob := string(input) + store := h.userdataStore + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB) + err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob)) + if err != nil { + return res, err + } + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB, []byte(yob)) if err != nil { return res, err } @@ -397,13 +414,20 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - if bytes.Equal(input, backOption) { - return res, nil + location := string(input) + store := h.userdataStore + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION) + err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) + if err != nil { + return res, err } - location := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location)) + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(location)) if err != nil { return res, err } @@ -421,14 +445,22 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re if !ok { return res, fmt.Errorf("missing session") } - if bytes.Equal(input, backOption) { - return res, nil - } gender := strings.Split(symbol, "_")[1] store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) - if err != nil { - return res, nil + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER) + err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) + if err != nil { + return res, err + } + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(gender)) + if err != nil { + return res, err + } } return res, nil @@ -442,12 +474,22 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - offerings := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)) + offerings := string(input) + store := h.userdataStore + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS) + err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { - return res, nil + return res, err + } + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)) + if err != nil { + return res, err } } From d25128287e5644f89a2199a619a87dcbb654df90 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 14:21:22 +0300 Subject: [PATCH 172/175] update profile information on individual node --- services/registration/enter_familyname.vis | 2 +- services/registration/enter_location.vis | 2 +- services/registration/enter_name.vis | 2 +- services/registration/enter_offerings.vis | 2 +- services/registration/enter_yob.vis | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/registration/enter_familyname.vis b/services/registration/enter_familyname.vis index 5a684ed..5db4c17 100644 --- a/services/registration/enter_familyname.vis +++ b/services/registration/enter_familyname.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_familyname flag_allow_update 1 MOUT back 0 HALT LOAD save_familyname 0 diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index c8da2dd..8966872 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_location flag_allow_update 1 MOUT back 0 HALT LOAD save_location 0 diff --git a/services/registration/enter_name.vis b/services/registration/enter_name.vis index 799b2a1..f853d0a 100644 --- a/services/registration/enter_name.vis +++ b/services/registration/enter_name.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_firstname flag_allow_update 1 MOUT back 0 HALT LOAD save_firstname 0 diff --git a/services/registration/enter_offerings.vis b/services/registration/enter_offerings.vis index 26e4b61..5cc7977 100644 --- a/services/registration/enter_offerings.vis +++ b/services/registration/enter_offerings.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_offerings flag_allow_update 1 LOAD save_offerings 0 MOUT back 0 HALT diff --git a/services/registration/enter_yob.vis b/services/registration/enter_yob.vis index 40bf3f4..c74aeed 100644 --- a/services/registration/enter_yob.vis +++ b/services/registration/enter_yob.vis @@ -1,10 +1,10 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 -LOAD save_yob 0 +CATCH update_yob flag_allow_update 1 MOUT back 0 HALT LOAD verify_yob 0 CATCH incorrect_date_format flag_incorrect_date_format 1 +LOAD save_yob 0 RELOAD save_yob INCMP _ 0 INCMP pin_entry * From b8bbd88078356e007809b0576200e7dad942d462 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 14:26:28 +0300 Subject: [PATCH 173/175] retain the temporary data for it to be overwritten --- internal/utils/vouchers.go | 17 +---------------- internal/utils/vouchers_test.go | 19 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go index 73a95a6..dc6cc4a 100644 --- a/internal/utils/vouchers.go +++ b/internal/utils/vouchers.go @@ -143,7 +143,7 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str return data, nil } -// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore. +// UpdateVoucherData sets the active voucher data in the DataStore. func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { // Active voucher data entries activeEntries := map[DataTyp][]byte{ @@ -153,14 +153,6 @@ func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, d DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress), } - // Clear temporary voucher data entries - tempEntries := map[DataTyp][]byte{ - DATA_TEMPORARY_SYM: []byte(""), - DATA_TEMPORARY_BAL: []byte(""), - DATA_TEMPORARY_DECIMAL: []byte(""), - DATA_TEMPORARY_ADDRESS: []byte(""), - } - // Write active data for key, value := range activeEntries { if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { @@ -168,12 +160,5 @@ func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, d } } - // Clear temporary data - for key, value := range tempEntries { - if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { - return err - } - } - return nil } diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go index 953182e..a609d27 100644 --- a/internal/utils/vouchers_test.go +++ b/internal/utils/vouchers_test.go @@ -31,11 +31,6 @@ func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) { return ctx, store } -// AssertEmptyValue checks if a value is empty/nil/zero -func AssertEmptyValue(t *testing.T, value []byte, msgAndArgs ...interface{}) { - assert.Equal(t, len(value), 0, msgAndArgs...) -} - func TestMatchVoucher(t *testing.T) { symbols := "1:SRF\n2:MILO" balances := "1:100\n2:200" @@ -205,18 +200,4 @@ func TestUpdateVoucherData(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key) } - - // Verify temporary data was cleared - tempKeys := []DataTyp{ - DATA_TEMPORARY_SYM, - DATA_TEMPORARY_BAL, - DATA_TEMPORARY_DECIMAL, - DATA_TEMPORARY_ADDRESS, - } - - for _, key := range tempKeys { - storedValue, err := store.ReadEntry(ctx, sessionId, key) - require.NoError(t, err) - AssertEmptyValue(t, storedValue, "Temporary data not cleared for key %v", key) - } } From e6a369dcddea338d04717babc1503fde10181368 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 14:44:42 +0300 Subject: [PATCH 174/175] remove unused code --- internal/utils/vouchers.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go index dc6cc4a..b027cc1 100644 --- a/internal/utils/vouchers.go +++ b/internal/utils/vouchers.go @@ -77,10 +77,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, for i, sym := range symList { parts := strings.SplitN(sym, ":", 2) - if len(parts) != 2 { - continue - } - + if input == parts[0] || strings.EqualFold(input, parts[1]) { symbol = parts[1] if i < len(balList) { From ce917d9e89b1591aff9fe1a181c29286957355fd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 17:05:06 +0300 Subject: [PATCH 175/175] update tests --- internal/handlers/ussd/menuhandler_test.go | 54 ++++++++++++++++------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index a960cfc..7c0689f 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -19,8 +19,8 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" - "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/common" + "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" @@ -174,8 +174,11 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) { } func TestSaveFirstname(t *testing.T) { - // Create a new instance of MockMyDataStore + // Create new mocks mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -183,11 +186,13 @@ func TestSaveFirstname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + flagManager: fm.parser, + st: mockState, } // Call the method @@ -204,6 +209,9 @@ func TestSaveFirstname(t *testing.T) { func TestSaveFamilyname(t *testing.T) { // Create a new instance of UserDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -211,11 +219,13 @@ func TestSaveFamilyname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -288,8 +298,11 @@ func TestSaveTemporaryPin(t *testing.T) { } func TestSaveYoB(t *testing.T) { - // Create a new instance of MockMyDataStore + // Create new instances mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -297,11 +310,13 @@ func TestSaveYoB(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_YOB, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_YOB, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -318,6 +333,9 @@ func TestSaveYoB(t *testing.T) { func TestSaveLocation(t *testing.T) { // Create a new instance of MockMyDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -325,11 +343,13 @@ func TestSaveLocation(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_LOCATION, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -346,6 +366,9 @@ func TestSaveLocation(t *testing.T) { func TestSaveOfferings(t *testing.T) { // Create a new instance of MockUserDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -353,11 +376,13 @@ func TestSaveOfferings(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -372,10 +397,12 @@ func TestSaveOfferings(t *testing.T) { } func TestSaveGender(t *testing.T) { - // Create a new instance of MockMyDataStore + // Create a new mock instances mockStore := new(mocks.MockUserDataStore) mockState := state.NewState(16) + fm, _ := NewFlagManager(flagsPath) + // Define the session ID and context sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) @@ -415,16 +442,17 @@ func TestSaveGender(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database if tt.expectCall { - expectedKey := utils.DATA_GENDER + expectedKey := utils.DATA_TEMPORARY_GENDER mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, st: mockState, + flagManager: fm.parser, } // Call the method @@ -435,9 +463,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) } }) }