From 188cb573dd713278b417d8fab9ea1bdc0ec6f9cb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 25 Sep 2024 13:27:13 +0300 Subject: [PATCH 001/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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/261] 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 72d5c186dd7a04b738262263ed5c785103f5e88f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:18:40 +0300 Subject: [PATCH 157/261] add admin privilege flag --- services/registration/pp.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index ec0d8c1..cee8d13 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -17,3 +17,5 @@ flag,flag_incorrect_date_format,23,this is set when the given year of birth is i 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_no_active_voucher,26,this is set when a user does not have an active voucher +flag,flag_admin_privilege,27,this is set when a user has admin privileges. + From 0cc0bdf9f71836301879eb068f22c96382f0bf96 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:19:35 +0300 Subject: [PATCH 158/261] add pin reset for others nodes --- services/registration/enter_others_new_pin | 1 + services/registration/enter_others_new_pin.vis | 8 ++++++++ services/registration/invalid_others_pin | 1 + services/registration/invalid_others_pin.vis | 5 +++++ services/registration/no_admin_privilege | 1 + services/registration/no_admin_privilege.vis | 5 +++++ services/registration/others_pin_mismatch | 1 + services/registration/others_pin_mismatch.vis | 5 +++++ services/registration/pin_management.vis | 3 +-- services/registration/pin_reset_result | 1 + services/registration/pin_reset_result.vis | 7 +++++++ 11 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 services/registration/enter_others_new_pin create mode 100644 services/registration/enter_others_new_pin.vis create mode 100644 services/registration/invalid_others_pin create mode 100644 services/registration/invalid_others_pin.vis create mode 100644 services/registration/no_admin_privilege create mode 100644 services/registration/no_admin_privilege.vis create mode 100644 services/registration/others_pin_mismatch create mode 100644 services/registration/others_pin_mismatch.vis create mode 100644 services/registration/pin_reset_result create mode 100644 services/registration/pin_reset_result.vis diff --git a/services/registration/enter_others_new_pin b/services/registration/enter_others_new_pin new file mode 100644 index 0000000..52ae664 --- /dev/null +++ b/services/registration/enter_others_new_pin @@ -0,0 +1 @@ +Please enter new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis new file mode 100644 index 0000000..aa2818d --- /dev/null +++ b/services/registration/enter_others_new_pin.vis @@ -0,0 +1,8 @@ +LOAD retrieve_blocked_number 0 +MAP retrieve_blocked_number +MOUT back 0 +HALT +INCMP _ 0 +LOAD verify_new_pin 0 +RELOAD verify_new_pin +INCMP * confirm_others_new_pin diff --git a/services/registration/invalid_others_pin b/services/registration/invalid_others_pin new file mode 100644 index 0000000..acdf45f --- /dev/null +++ b/services/registration/invalid_others_pin @@ -0,0 +1 @@ +The PIN you have entered is invalid.Please try a 4 digit number instead. \ No newline at end of file diff --git a/services/registration/invalid_others_pin.vis b/services/registration/invalid_others_pin.vis new file mode 100644 index 0000000..d218e6d --- /dev/null +++ b/services/registration/invalid_others_pin.vis @@ -0,0 +1,5 @@ +MOUT retry 1 +MOUT quit 9 +HALT +INCMP enter_others_new_pin 1 +INCMP quit 9 diff --git a/services/registration/no_admin_privilege b/services/registration/no_admin_privilege new file mode 100644 index 0000000..27901dc --- /dev/null +++ b/services/registration/no_admin_privilege @@ -0,0 +1 @@ +You do not have privileges to perform this action diff --git a/services/registration/no_admin_privilege.vis b/services/registration/no_admin_privilege.vis new file mode 100644 index 0000000..3cf1e4c --- /dev/null +++ b/services/registration/no_admin_privilege.vis @@ -0,0 +1,5 @@ +MOUT quit 9 +MOUT back 0 +HALT +INCMP pin_management 0 +INCMP quit 9 diff --git a/services/registration/others_pin_mismatch b/services/registration/others_pin_mismatch new file mode 100644 index 0000000..deb9fe5 --- /dev/null +++ b/services/registration/others_pin_mismatch @@ -0,0 +1 @@ +The PIN you have entered is not a match diff --git a/services/registration/others_pin_mismatch.vis b/services/registration/others_pin_mismatch.vis new file mode 100644 index 0000000..37b3deb --- /dev/null +++ b/services/registration/others_pin_mismatch.vis @@ -0,0 +1,5 @@ +MOUT retry 1 +MOUT quit 9 +HALT +INCMP _ 1 +INCMP quit 9 diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index 3b33dad..b196a50 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -1,8 +1,7 @@ MOUT change_pin 1 MOUT reset_pin 2 -MOUT guard_pin 3 MOUT back 0 HALT INCMP _ 0 INCMP old_pin 1 - +INCMP enter_other_number 2 diff --git a/services/registration/pin_reset_result b/services/registration/pin_reset_result new file mode 100644 index 0000000..60554b9 --- /dev/null +++ b/services/registration/pin_reset_result @@ -0,0 +1 @@ +PIN reset request for {{.retrieve_blocked_number}} was successful \ No newline at end of file diff --git a/services/registration/pin_reset_result.vis b/services/registration/pin_reset_result.vis new file mode 100644 index 0000000..ed71200 --- /dev/null +++ b/services/registration/pin_reset_result.vis @@ -0,0 +1,7 @@ +LOAD retrieve_blocked_number 0 +MAP retrieve_blocked_number +MOUT back 0 +MOUT quit 9 +HALT +INCMP pin_management 0 +INCMP quit 9 From 3de46cef5e2758b228ea49bf597d2627185f3aa1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:45:08 +0300 Subject: [PATCH 159/261] setup pin reset nodes --- services/registration/confirm_others_new_pin | 1 + services/registration/confirm_others_new_pin.vis | 13 +++++++++++++ services/registration/confirm_pin_change.vis | 2 -- services/registration/enter_other_number | 1 + services/registration/enter_other_number.vis | 9 +++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 services/registration/confirm_others_new_pin create mode 100644 services/registration/confirm_others_new_pin.vis create mode 100644 services/registration/enter_other_number create mode 100644 services/registration/enter_other_number.vis diff --git a/services/registration/confirm_others_new_pin b/services/registration/confirm_others_new_pin new file mode 100644 index 0000000..46c3275 --- /dev/null +++ b/services/registration/confirm_others_new_pin @@ -0,0 +1 @@ +Please confirm new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis new file mode 100644 index 0000000..be6734f --- /dev/null +++ b/services/registration/confirm_others_new_pin.vis @@ -0,0 +1,13 @@ +LOAD retrieve_blocked_number 0 +MAP retrieve_blocked_number +CATCH invalid_others_pin flag_valid_pin 0 +LOAD save_temporary_pin 6 +RELOAD save_temporary_pin +CATCH pin_reset_result flag_account_authorized 1 +MOUT back 0 +HALT +INCMP _ 0 +LOAD check_pin_mismatch 0 +RELOAD check_pin_mismatch +CATCH others_pin_mismatch flag_pin_mismatch 1 +INCMP pin_entry * diff --git a/services/registration/confirm_pin_change.vis b/services/registration/confirm_pin_change.vis index 7691e01..cf485a1 100644 --- a/services/registration/confirm_pin_change.vis +++ b/services/registration/confirm_pin_change.vis @@ -3,5 +3,3 @@ MOUT back 0 HALT INCMP _ 0 INCMP * pin_reset_success - - diff --git a/services/registration/enter_other_number b/services/registration/enter_other_number new file mode 100644 index 0000000..1c4a481 --- /dev/null +++ b/services/registration/enter_other_number @@ -0,0 +1 @@ +Enter other's phone number: \ No newline at end of file diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis new file mode 100644 index 0000000..41c711a --- /dev/null +++ b/services/registration/enter_other_number.vis @@ -0,0 +1,9 @@ +CATCH no_admin_privilege flag_admin_privilege 0 +LOAD reset_account_authorized 6 +RELOAD reset_account_authorized +MOUT back 0 +HALT +INCMP _ 0 +LOAD validate_blocked_number 0 +RELOAD validate_blocked_number +INCMP enter_others_new_pin * From e4c2f644f378ffc15429a2614aebdcb12ce142f4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:47:56 +0300 Subject: [PATCH 160/261] define a key to hold number during pin reset --- internal/utils/db.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/utils/db.go b/internal/utils/db.go index 45e7681..57f3289 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_BLOCKED_NUMBER ) func typToBytes(typ DataTyp) []byte { From 26073c8000c030cb6d03766bac6dffe851aca88f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:49:20 +0300 Subject: [PATCH 161/261] define handler functions required to reset others pin --- internal/handlers/handlerservice.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 311e694..a30f1b2 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -54,7 +54,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { } func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, accountService) if err != nil { return nil, err } @@ -98,6 +98,10 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) + ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin) + ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch) + ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) + ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) return ussdHandlers, nil } From 5d294b663cc1fc3621eaba97b9622f41b44021a5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 28 Oct 2024 16:21:31 +0300 Subject: [PATCH 162/261] 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 163/261] 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 164/261] 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 165/261] 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 aec0abb2b632af7cda2296a8eb8bcb8b5e6aaac6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 16:34:33 +0300 Subject: [PATCH 166/261] setup an admin flag --- cmd/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/main.go b/cmd/main.go index 21ca0c3..e79176c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -33,10 +33,12 @@ func main() { var size uint var sessionId string var database string + var isAdmin bool 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(&isAdmin, "admin", false, "if user has admin previleges") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.Parse() @@ -46,6 +48,7 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) + ctx = context.WithValue(ctx, "Admin", isAdmin) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ From b97965193bb53185221a77d846e3d40e6d7ca005 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 16:37:23 +0300 Subject: [PATCH 167/261] add pin reset for others handling --- internal/handlers/ussd/menuhandler.go | 118 ++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f4d279b..ae50724 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -27,11 +27,12 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("ussdmenuhandler") - scriptDir = path.Join("services", "registration") - translationDir = path.Join(scriptDir, "locale") - okResponse *api.OKResponse - errResponse *api.ErrResponse + logg = logging.NewVanilla().WithDomain("ussdmenuhandler") + scriptDir = path.Join("services", "registration") + translationDir = path.Join(scriptDir, "locale") + PINChangePrivilege byte = 1 + okResponse *api.OKResponse + errResponse *api.ErrResponse ) // FlagManager handles centralized flag management @@ -98,20 +99,41 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { return h } +func setAdminPrevilege(ctx context.Context, store utils.DataStore) error { + var err error + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return fmt.Errorf("missing session") + } + prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) + err = prefixdb.Put(ctx, []byte(sessionId), []byte("1")) + if err != nil { + return err + } + return nil +} + func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) { var r resource.Result - if h.pe == nil { logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca) return r, nil } + h.st = h.pe.GetState() h.ca = h.pe.GetMemory() + if h.st == nil || h.ca == nil { logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) return r, fmt.Errorf("cannot get state and memory for handler") } h.pe = nil + store := h.userdataStore + err := setAdminPrevilege(ctx, store) + if err != nil { + return r, fmt.Errorf("failed to set previlege level") + } logg.DebugCtxf(ctx, "handler has been initialized", "state", h.st, "cache", h.ca) @@ -189,6 +211,26 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, nil } +func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) { + res := resource.Result{} + flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + if err != nil { + return res, err + } + if bytes.Equal(temporaryPin, input) { + res.FlagReset = append(res.FlagReset, flag_pin_mismatch) + } else { + res.FlagSet = append(res.FlagSet, flag_pin_mismatch) + } + 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) @@ -284,7 +326,6 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte if err != nil { return res, err } - if bytes.Equal(input, temporaryPin) { res.FlagSet = []uint32{flag_valid_pin} res.FlagReset = []uint32{flag_pin_mismatch} @@ -444,6 +485,14 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt return res, nil } +// ResetAllowUpdate resets the allowupdate flag that allows a user to update profile data. +func (h *Handlers) ResetValidPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") + res.FlagReset = append(res.FlagReset, flag_valid_pin) + return res, nil +} + // 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 @@ -501,6 +550,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, nil } } else { + fmt.Println("Authorizing the account else") return res, nil } return res, nil @@ -522,17 +572,37 @@ 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") + flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } + isAdmin, _ := ctx.Value("Admin").(bool) store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { return res, err } + if isAdmin { + setAdminPrevilege(ctx, store) + } + prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) + accessLevel, err := prefixdb.Get(ctx, []byte(sessionId)) + if err != nil { + if !db.IsNotFound(err) { + return res, nil + } + } + isPrevileged := bytes.Equal(accessLevel, []byte("1")) + + if isPrevileged { + //Set Admin privilege Flag + res.FlagSet = append(res.FlagSet, flag_admin_privilege) + } + okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) + if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err @@ -588,7 +658,6 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res var err error flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") - date := string(input) _, err = strconv.Atoi(date) if err != nil { @@ -694,6 +763,22 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, nil } +func (h *Handlers) ValidateBlockedNumber(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") + } + blockedNumber := string(input) + err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) + if err != nil { + return res, nil + } + 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 @@ -865,6 +950,22 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, nil } +// RetrieveBlockedNumber gets the current number during the pin reset for other's is in progress. +func (h *Handlers) RetrieveBlockedNumber(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 + blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + + res.Content = string(blockedNumber) + + return res, nil +} + // GetSender returns the sessionId (phoneNumber) func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1102,7 +1203,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) if err != nil { return res, nil } - err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList)) if err != nil { return res, nil From e338ce00257316f6a9d107d81bfde9043bdea0e7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:14:49 +0300 Subject: [PATCH 168/261] load and reload only after input --- services/registration/enter_familyname.vis | 3 +-- 1 file changed, 1 insertion(+), 2 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 * From ca13d9155c000a615a89fb6d0fce44765e67c12d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:15:22 +0300 Subject: [PATCH 169/261] replace _ with explicit back node --- services/registration/pin_management.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index b196a50..c0adc5c 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -2,6 +2,6 @@ MOUT change_pin 1 MOUT reset_pin 2 MOUT back 0 HALT -INCMP _ 0 +INCMP my_account 0 INCMP old_pin 1 INCMP enter_other_number 2 From c9bb93ede6c0deaba60868c4eb2f39a717371f59 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:15:41 +0300 Subject: [PATCH 170/261] create a simple admin store for phone numbers --- internal/utils/adminstore.go | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 internal/utils/adminstore.go diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go new file mode 100644 index 0000000..98241a6 --- /dev/null +++ b/internal/utils/adminstore.go @@ -0,0 +1,68 @@ +package utils + +import ( + "bufio" + "os" + "strconv" +) + +type AdminStore struct { + filePath string +} + +// Creates a new Admin store +func NewAdminStore(filePath string) *AdminStore { + return &AdminStore{filePath: filePath} +} + +// Seed saves a list of phonumbers with admin privileges +func (as *AdminStore) Seed() error { + adminNumbers := []int64{254705136690, 123456789012, 987654321098} + file, err := os.Create(as.filePath) + if err != nil { + return err + } + defer file.Close() + + writer := bufio.NewWriter(file) + for _, num := range adminNumbers { + _, err := writer.WriteString(strconv.FormatInt(num, 10) + "\n") + if err != nil { + return err + } + } + return writer.Flush() +} + + +func (as *AdminStore) load() ([]int64, error) { + file, err := os.Open(as.filePath) + if err != nil { + return nil, err + } + defer file.Close() + + var numbers []int64 + scanner := bufio.NewScanner(file) + for scanner.Scan() { + num, err := strconv.ParseInt(scanner.Text(), 10, 64) + if err != nil { + return nil, err + } + numbers = append(numbers, num) + } + return numbers, scanner.Err() +} + +func (as *AdminStore) IsAdmin(phoneNumber int64) (bool, error) { + phoneNumbers, err := as.load() + if err != nil { + return false, err + } + for _, phonenumber := range phoneNumbers { + if phonenumber == phoneNumber { + return true, nil + } + } + return false, nil +} From 41da099933a9ee36605258179c305bc5965720d8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:24:13 +0300 Subject: [PATCH 171/261] remove the admin flag,setup an admin store --- cmd/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index e79176c..4d23011 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,7 @@ 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/utils" ) var ( @@ -33,12 +34,10 @@ func main() { var size uint var sessionId string var database string - var isAdmin bool 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(&isAdmin, "admin", false, "if user has admin previleges") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.Parse() @@ -48,9 +47,11 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) - ctx = context.WithValue(ctx, "Admin", isAdmin) pfp := path.Join(scriptDir, "pp.csv") + as := utils.NewAdminStore("admin_numbers.txt") + as.Seed() + cfg := engine.Config{ Root: "root", SessionId: sessionId, From d83962c0ba7481327d529f612df96288f09327ff Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 14:26:24 +0300 Subject: [PATCH 172/261] load admin numbers defined in the .env --- internal/utils/adminstore.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go index 98241a6..ddd6bd9 100644 --- a/internal/utils/adminstore.go +++ b/internal/utils/adminstore.go @@ -2,8 +2,12 @@ package utils import ( "bufio" + "log" "os" "strconv" + "strings" + + "git.grassecon.net/urdt/ussd/initializers" ) type AdminStore struct { @@ -15,9 +19,18 @@ func NewAdminStore(filePath string) *AdminStore { return &AdminStore{filePath: filePath} } -// Seed saves a list of phonumbers with admin privileges +// Seed initializes a list of phonenumbers with admin privileges func (as *AdminStore) Seed() error { - adminNumbers := []int64{254705136690, 123456789012, 987654321098} + var adminNumbers []int64 + + numbersEnv := initializers.GetEnv("ADMIN_NUMBERS", "") + for _, numStr := range strings.Split(numbersEnv, ",") { + if num, err := strconv.ParseInt(strings.TrimSpace(numStr), 10, 64); err == nil { + adminNumbers = append(adminNumbers, num) + } else { + log.Printf("Skipping invalid number: %s", numStr) + } + } file, err := os.Create(as.filePath) if err != nil { return err @@ -34,7 +47,6 @@ func (as *AdminStore) Seed() error { return writer.Flush() } - func (as *AdminStore) load() ([]int64, error) { file, err := os.Open(as.filePath) if err != nil { From 5fd3eb3c292f5640cd556a1063e1ab5f220d3e23 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 14:28:58 +0300 Subject: [PATCH 173/261] set admin privilege flag --- internal/handlers/ussd/menuhandler.go | 55 ++++++++------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ae50724..d397eff 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -99,21 +99,6 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { return h } -func setAdminPrevilege(ctx context.Context, store utils.DataStore) error { - var err error - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return fmt.Errorf("missing session") - } - prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) - err = prefixdb.Put(ctx, []byte(sessionId), []byte("1")) - if err != nil { - return err - } - return nil -} - func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) { var r resource.Result if h.pe == nil { @@ -124,16 +109,24 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource h.st = h.pe.GetState() h.ca = h.pe.GetMemory() + sessionId, _ := ctx.Value("SessionId").(string) + flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") + + number, _ := strconv.ParseInt(sessionId, 10, 64) + as := utils.NewAdminStore("admin_numbers.txt") + isAdmin, _ := as.IsAdmin(number) + + if isAdmin { + r.FlagSet = append(r.FlagSet, flag_admin_privilege) + } else { + r.FlagReset = append(r.FlagReset, flag_admin_privilege) + } + if h.st == nil || h.ca == nil { logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) return r, fmt.Errorf("cannot get state and memory for handler") } h.pe = nil - store := h.userdataStore - err := setAdminPrevilege(ctx, store) - if err != nil { - return r, fmt.Errorf("failed to set previlege level") - } logg.DebugCtxf(ctx, "handler has been initialized", "state", h.st, "cache", h.ca) @@ -403,6 +396,7 @@ 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 @@ -550,7 +544,6 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, nil } } else { - fmt.Println("Authorizing the account else") return res, nil } return res, nil @@ -572,37 +565,19 @@ 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") - flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - isAdmin, _ := ctx.Value("Admin").(bool) + store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { return res, err } - if isAdmin { - setAdminPrevilege(ctx, store) - } - prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) - accessLevel, err := prefixdb.Get(ctx, []byte(sessionId)) - if err != nil { - if !db.IsNotFound(err) { - return res, nil - } - } - isPrevileged := bytes.Equal(accessLevel, []byte("1")) - - if isPrevileged { - //Set Admin privilege Flag - res.FlagSet = append(res.FlagSet, flag_admin_privilege) - } okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) - if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err From 124049c92453ff6db90f91b2fe0fce4554a2061a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 14:32:17 +0300 Subject: [PATCH 174/261] add admin number defination in env --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index ab370a7..4d86f8b 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,7 @@ 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 + + +#numbers with privileges to reset others pin +ADMIN_NUMBERS=254051722XXX,255012221XXX \ No newline at end of file From e96c8743004550e0738494b9cd3d0899d7be7885 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 15:02:40 +0300 Subject: [PATCH 175/261] repeat same node on invalid option input --- services/registration/pin_management.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index c0adc5c..5eb7d5a 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -5,3 +5,4 @@ HALT INCMP my_account 0 INCMP old_pin 1 INCMP enter_other_number 2 +INCMP . * From 449f90c95b6a4f41bd2d8c1501a99ae39d052fc7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:17:03 +0300 Subject: [PATCH 176/261] add flag to catch unregistred numbers --- services/registration/pp.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index cee8d13..406cc22 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -18,4 +18,4 @@ 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_no_active_voucher,26,this is set when a user does not have an active voucher flag,flag_admin_privilege,27,this is set when a user has admin privileges. - +flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action From 6998c30dd1614077ddcf36ac5d1fb4ab3a4ff338 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:18:01 +0300 Subject: [PATCH 177/261] add node to handle unregistered phone numbers --- services/registration/unregistered_number | 1 + services/registration/unregistered_number.vis | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 services/registration/unregistered_number create mode 100644 services/registration/unregistered_number.vis diff --git a/services/registration/unregistered_number b/services/registration/unregistered_number new file mode 100644 index 0000000..9cc33d7 --- /dev/null +++ b/services/registration/unregistered_number @@ -0,0 +1 @@ +The number you have entered is either not registered with Sarafu or is invalid. \ No newline at end of file diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis new file mode 100644 index 0000000..f7a6a9c --- /dev/null +++ b/services/registration/unregistered_number.vis @@ -0,0 +1,7 @@ +LOAD reset_unregistered_number 0 +RELOAD reset_unregistered_number +MOUT retry 1 +MOUT quit 9 +HALT +INCMP enter_other_number 1 +INCMP quit 9 From 534d75631836a50bf00ebe194b33c6d061726853 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:18:39 +0300 Subject: [PATCH 178/261] catch unregistred phone numbers --- services/registration/enter_others_new_pin.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index aa2818d..1577ff1 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,3 +1,4 @@ +CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 From 91b85af11a223cb66de5b6cc2af900296556b3b0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:34:34 +0300 Subject: [PATCH 179/261] add reset unregistered number --- internal/handlers/handlerservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index a30f1b2..b4cd592 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -102,6 +102,7 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch) ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) + ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) return ussdHandlers, nil } From 106983a3940fc0b2066e0486fd1b47dcd1239345 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:35:01 +0300 Subject: [PATCH 180/261] use explicit back to node --- services/registration/enter_other_number.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index 41c711a..7f413da 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -3,7 +3,7 @@ LOAD reset_account_authorized 6 RELOAD reset_account_authorized MOUT back 0 HALT -INCMP _ 0 LOAD validate_blocked_number 0 RELOAD validate_blocked_number +INCMP pin_management 0 INCMP enter_others_new_pin * From d0ad6395b5670501b543e61e2a5ef2c8c5acf2ca Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:35:42 +0300 Subject: [PATCH 181/261] add check for unregistered phone numbers --- internal/handlers/ussd/menuhandler.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d395a8b..715a654 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -748,15 +748,35 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, nil } +func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") + res.FlagReset = append(res.FlagReset, flag_unregistered_number) + return res, nil +} + func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error + + flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") + store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } blockedNumber := string(input) + _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + logg.Printf(logging.LVL_INFO, "Invalid or unregistered number") + res.FlagSet = append(res.FlagSet, flag_unregistered_number) + return res, nil + } else { + return res, err + } + } err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) if err != nil { return res, nil From f37483e2f04d4fa4ead95223ff2c0acc33518ff8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:15:31 +0300 Subject: [PATCH 182/261] use _ for back navigation --- services/registration/unregistered_number.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis index f7a6a9c..114113f 100644 --- a/services/registration/unregistered_number.vis +++ b/services/registration/unregistered_number.vis @@ -3,5 +3,5 @@ RELOAD reset_unregistered_number MOUT retry 1 MOUT quit 9 HALT -INCMP enter_other_number 1 +INCMP _ 1 INCMP quit 9 From 7597b96daec3367a8a09dff0ee24f95a616107a2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:16:34 +0300 Subject: [PATCH 183/261] remove catch for unregistered number --- services/registration/enter_others_new_pin.vis | 1 - 1 file changed, 1 deletion(-) diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index 1577ff1..aa2818d 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,4 +1,3 @@ -CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 From 5a0563df94b67fade2498e8f37eef91f82a95da4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:17:43 +0300 Subject: [PATCH 184/261] group regex,check for valid number against the regex --- internal/handlers/ussd/menuhandler.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 715a654..70510ee 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -35,6 +35,12 @@ var ( backOption = []byte("0") ) +// Define the regex patterns as constants +const ( + phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}` + pinPattern = `^\d{4}$` +) + // FlagManager handles centralized flag management type FlagManager struct { parser *asm.FlagParser @@ -82,15 +88,17 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s return h, nil } -// Define the regex pattern as a constant -const pinPattern = `^\d{4}$` - // isValidPIN checks whether the given input is a 4 digit number func isValidPIN(pin string) bool { match, _ := regexp.MatchString(pinPattern, pin) return match } +func isValidPhoneNumber(phonenumber string) bool { + match, _ := regexp.MatchString(phoneRegex, phonenumber) + return match +} + func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { if h.pe != nil { panic("persister already set") @@ -255,7 +263,6 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt } flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - accountPIN := string(input) // Validate that the PIN is a 4-digit number @@ -768,6 +775,9 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input } blockedNumber := string(input) _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) + if !isValidPhoneNumber(blockedNumber) { + return res, nil + } if err != nil { if db.IsNotFound(err) { logg.Printf(logging.LVL_INFO, "Invalid or unregistered number") From 0a97f610a47746c25cc7748ebbf29937e3bc11a2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:23:22 +0300 Subject: [PATCH 185/261] catch unregistred number --- services/registration/enter_other_number.vis | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index 7f413da..e2fa243 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -1,9 +1,10 @@ CATCH no_admin_privilege flag_admin_privilege 0 -LOAD reset_account_authorized 6 +LOAD reset_account_authorized 0 RELOAD reset_account_authorized MOUT back 0 HALT -LOAD validate_blocked_number 0 +INCMP _ 0 +LOAD validate_blocked_number 20 RELOAD validate_blocked_number -INCMP pin_management 0 +CATCH unregistered_number flag_unregistered_number 1 INCMP enter_others_new_pin * From 0480c02633067fa5638217327fb24b9d1fc62869 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 00:52:23 +0300 Subject: [PATCH 186/261] 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 187/261] 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 188/261] 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 dd764a2e24514b9799e78973c7ccb80c510f1593 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 01:28:55 +0000 Subject: [PATCH 189/261] Export db datatypes,tools --- {internal/utils => common}/db.go | 2 +- internal/handlers/ussd/menuhandler.go | 108 ++++++++++----------- internal/handlers/ussd/menuhandler_test.go | 97 +++++++++--------- internal/testutil/mocks/userdbmock.go | 6 +- internal/utils/userStore.go | 14 +-- 5 files changed, 114 insertions(+), 113 deletions(-) rename {internal/utils => common}/db.go (97%) diff --git a/internal/utils/db.go b/common/db.go similarity index 97% rename from internal/utils/db.go rename to common/db.go index 2c1e6fa..349d3aa 100644 --- a/internal/utils/db.go +++ b/common/db.go @@ -1,4 +1,4 @@ -package utils +package common import ( "encoding/binary" diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dae4236..e2bcfaf 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -151,9 +151,9 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r trackingId := okResponse.Result["trackingId"].(string) publicKey := okResponse.Result["publicKey"].(string) - data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: trackingId, - utils.DATA_PUBLIC_KEY: publicKey, + data := map[common.DataTyp]string{ + common.DATA_TRACKING_ID: trackingId, + common.DATA_PUBLIC_KEY: publicKey, } store := h.userdataStore for key, value := range data { @@ -166,7 +166,7 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r if err != nil { return err } - err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) + err = store.WriteEntry(ctx, publicKeyNormalized, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) if err != nil { return err } @@ -186,7 +186,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, fmt.Errorf("missing session") } store := h.userdataStore - _, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_CREATED) + _, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED) if err != nil { if db.IsNotFound(err) { logg.Printf(logging.LVL_INFO, "Creating an account because it doesn't exist") @@ -242,7 +242,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_incorrect_pin) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_PIN, []byte(accountPIN)) if err != nil { return res, err } @@ -259,7 +259,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_PIN) if err != nil { return res, err } @@ -268,7 +268,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt } else { res.FlagSet = append(res.FlagSet, flag_pin_mismatch) } - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, err } @@ -290,7 +290,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_PIN) if err != nil { return res, err } @@ -303,7 +303,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte res.FlagSet = []uint32{flag_pin_mismatch} } - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, err } @@ -335,7 +335,7 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) } firstName := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(firstName)) if err != nil { return res, err } @@ -358,7 +358,7 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) } familyName := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(familyName)) if err != nil { return res, err } @@ -380,7 +380,7 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou if len(input) == 4 { yob := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob)) + err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(yob)) if err != nil { return res, err } @@ -403,7 +403,7 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( } location := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location)) + err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(location)) if err != nil { return res, err } @@ -426,7 +426,7 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re } gender := strings.Split(symbol, "_")[1] store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) + err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(gender)) if err != nil { return res, nil } @@ -445,7 +445,7 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) if len(input) > 0 { offerings := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)) + err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(offerings)) if err != nil { return res, nil } @@ -481,7 +481,7 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, _ := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) res.Content = string(publicKey) @@ -502,7 +502,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") store := h.userdataStore - AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) + AccountPin, err := store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN) if err != nil { return res, err } @@ -548,7 +548,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b return res, fmt.Errorf("missing session") } store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, err } @@ -654,7 +654,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( store := h.userdataStore // get the active sym and active balance - activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { if db.IsNotFound(err) { balance := "0.00" @@ -665,7 +665,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { return res, err } @@ -687,7 +687,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input balanceType := strings.Split(symbol, "_")[0] store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, err } @@ -737,7 +737,7 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, nil } store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recipient)) + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) if err != nil { return res, nil } @@ -760,12 +760,12 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) if err != nil { return res, nil } - err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte("")) if err != nil { return res, nil } @@ -787,7 +787,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) if err != nil { return res, nil } @@ -809,7 +809,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res } store := h.userdataStore - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { return res, err } @@ -834,7 +834,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) var balanceValue float64 // retrieve the active balance - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { return res, err } @@ -860,7 +860,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) // Format the amount with 2 decimal places before saving formattedAmount := fmt.Sprintf("%.2f", inputAmount) - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(formattedAmount)) + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(formattedAmount)) if err != nil { return res, err } @@ -878,7 +878,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, fmt.Errorf("missing session") } store := h.userdataStore - recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) res.Content = string(recipient) @@ -910,12 +910,12 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res store := h.userdataStore // retrieve the active symbol - activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { return res, err } - amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) + amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) res.Content = fmt.Sprintf("%s %s", string(amount), string(activeSym)) @@ -939,11 +939,11 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] // Use the amount, recipient and sender to call the API and initialize the transaction store := h.userdataStore - amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) + amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) - recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) - activeSym, _ := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + activeSym, _ := store.ReadEntry(ctx, sessionId, common.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)) @@ -983,12 +983,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) } store := h.userdataStore // Retrieve user data as strings with fallback to defaultValue - firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME)) - familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME)) - yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_YOB)) - gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_GENDER)) - location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION)) - offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS)) + firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME)) + familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME)) + yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_YOB)) + gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_GENDER)) + location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_LOCATION)) + offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS)) // Construct the full name name := defaultValue @@ -1045,11 +1045,11 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by 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) + _, err = store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { if db.IsNotFound(err) { - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, nil } @@ -1072,12 +1072,12 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by defaultBal := firstVoucher.Balance // set the active symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(defaultSym)) + err = store.WriteEntry(ctx, sessionId, common.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)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) if err != nil { return res, err } @@ -1103,7 +1103,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) } store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, nil } @@ -1207,11 +1207,11 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r // 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)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) if err != nil { return res, err } - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_BAL, []byte(matchedBalance)) if err != nil { return res, err } @@ -1269,34 +1269,34 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re } // get the current temporary symbol - temporarySym, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM) + temporarySym, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_SYM) if err != nil { return res, err } // get the current temporary balance - temporaryBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL) + temporaryBal, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_BAL) if err != nil { return res, err } // set the active symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym)) + err = store.WriteEntry(ctx, sessionId, common.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)) + err = store.WriteEntry(ctx, sessionId, common.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("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_SYM, []byte("")) if err != nil { return res, err } // reset the temporary balance - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_BAL, []byte("")) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 0b70e80..b83fa61 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -19,7 +19,6 @@ 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" "github.com/alecthomas/assert/v2" "github.com/grassrootseconomics/eth-custodial/pkg/api" @@ -119,12 +118,12 @@ func TestCreateAccount(t *testing.T) { } publicKey := tt.serverResponse.Result["publicKey"].(string) - data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), - utils.DATA_PUBLIC_KEY: publicKey, + data := map[common.DataTyp]string{ + common.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), + common.DATA_PUBLIC_KEY: publicKey, } - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) mockCreateAccountService.On("CreateAccount").Return(tt.serverResponse, nil) for key, value := range data { @@ -135,7 +134,7 @@ func TestCreateAccount(t *testing.T) { t.Fatal(err) } - mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)).Return(nil) + mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, common.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")) @@ -181,7 +180,7 @@ 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, common.DATA_FIRST_NAME, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -209,7 +208,7 @@ 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, common.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -269,7 +268,7 @@ func TestSaveTemporaryPin(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_TEMPORARY_PIN, []byte(tt.input)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -295,7 +294,7 @@ 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, common.DATA_YOB, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -323,7 +322,7 @@ 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, common.DATA_LOCATION, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -351,7 +350,7 @@ 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, common.DATA_OFFERINGS, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -413,10 +412,10 @@ 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 := common.DATA_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, common.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store @@ -433,9 +432,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, common.DATA_GENDER, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) } }) } @@ -469,7 +468,7 @@ func TestCheckIdentifier(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) + mockStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) // Create the Handlers instance with the mock store h := &Handlers{ @@ -519,8 +518,8 @@ func TestGetAmount(t *testing.T) { activeSym := "SRF" // Set up the expected behavior of the mock - 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) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACTIVE_SYM).Return([]byte(activeSym), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_AMOUNT).Return([]byte(amount), nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -546,7 +545,7 @@ func TestGetRecipient(t *testing.T) { recepient := "0xcasgatweksalw1018221" // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return([]byte(recepient), nil) + mockStore.On("ReadEntry", ctx, sessionId, common.DATA_RECIPIENT).Return([]byte(recepient), nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -828,7 +827,7 @@ func TestAuthorize(t *testing.T) { // Define session ID and mock data sessionId := "session123" - typ := utils.DATA_ACCOUNT_PIN + typ := common.DATA_ACCOUNT_PIN h := &Handlers{ userdataStore: mockDataStore, @@ -1011,7 +1010,7 @@ func TestVerifyCreatePin(t *testing.T) { }, } - typ := utils.DATA_TEMPORARY_PIN + typ := common.DATA_TEMPORARY_PIN for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1020,7 +1019,7 @@ func TestVerifyCreatePin(t *testing.T) { 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) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(firstSetPin)).Return(nil) // Call the method under test res, err := h.VerifyCreatePin(ctx, "verify_create_pin", []byte(tt.input)) @@ -1141,11 +1140,11 @@ func TestCheckAccountStatus(t *testing.T) { status := tt.response.Result.Transaction.Status // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.input, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.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() + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() // Call the method under test res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) @@ -1201,8 +1200,8 @@ func TestTransactionReset(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_AMOUNT, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_RECIPIENT, []byte("")).Return(nil) // Call the method under test res, _ := h.TransactionReset(ctx, "transaction_reset", tt.input) @@ -1257,7 +1256,7 @@ func TestResetInvalidAmount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_AMOUNT, []byte("")).Return(nil) // Call the method under test res, _ := h.ResetTransactionAmount(ctx, "transaction_reset_amount", tt.input) @@ -1320,9 +1319,9 @@ func TestInitiateTransaction(t *testing.T) { 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_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) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_AMOUNT).Return(tt.Amount, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_RECIPIENT).Return(tt.Recipient, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACTIVE_SYM).Return(tt.ActiveSym, nil) // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1502,10 +1501,10 @@ func TestValidateAmount(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Mock behavior for active balance retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.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() + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_AMOUNT, tt.input).Return(nil).Maybe() // Call the method under test res, _ := h.ValidateAmount(ctx, "test_validate_amount", tt.input) @@ -1559,7 +1558,7 @@ func TestValidateRecipient(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, tt.input).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_RECIPIENT, tt.input).Return(nil) // Create the Handlers instance with the mock flag manager h := &Handlers{ @@ -1613,8 +1612,8 @@ func TestCheckBalance(t *testing.T) { } // 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) + mockDataStore.On("ReadEntry", ctx, tt.sessionId, common.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) + mockDataStore.On("ReadEntry", ctx, tt.sessionId, common.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) res, err := h.CheckBalance(ctx, "check_balance", []byte("")) @@ -1648,13 +1647,13 @@ func TestGetProfile(t *testing.T) { tests := []struct { name string languageCode string - keys []utils.DataTyp + keys []common.DataTyp profileInfo []string result resource.Result }{ { 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}, + keys: []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "eng", result: resource.Result{ @@ -1666,7 +1665,7 @@ func TestGetProfile(t *testing.T) { }, { 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}, + keys: []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "swa", result: resource.Result{ @@ -1678,7 +1677,7 @@ func TestGetProfile(t *testing.T) { }, { 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}, + keys: []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "nor", result: resource.Result{ @@ -1802,9 +1801,9 @@ func TestConfirmPin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) @@ -1829,12 +1828,12 @@ func TestSetVoucher(t *testing.T) { 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) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_SYM).Return(temporarySym, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_BAL).Return(temporaryBal, nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_SYM, temporarySym).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_BAL, temporaryBal).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_SYM, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_BAL, []byte("")).Return(nil) h := &Handlers{ userdataStore: mockDataStore, @@ -1919,7 +1918,7 @@ func TestFetchCustodialBalances(t *testing.T) { } // Set up the expected behavior of the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) // Call the method diff --git a/internal/testutil/mocks/userdbmock.go b/internal/testutil/mocks/userdbmock.go index ff3f18d..7892179 100644 --- a/internal/testutil/mocks/userdbmock.go +++ b/internal/testutil/mocks/userdbmock.go @@ -4,7 +4,7 @@ import ( "context" "git.defalsify.org/vise.git/db" - "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/common" "github.com/stretchr/testify/mock" ) @@ -13,12 +13,12 @@ type MockUserDataStore struct { mock.Mock } -func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) { +func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ common.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 { +func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error { args := m.Called(ctx, sessionId, typ, value) return args.Error(0) } diff --git a/internal/utils/userStore.go b/internal/utils/userStore.go index a1485b1..13682b7 100644 --- a/internal/utils/userStore.go +++ b/internal/utils/userStore.go @@ -3,13 +3,15 @@ package utils import ( "context" + "git.grassecon.net/urdt/ussd/common" + "git.defalsify.org/vise.git/db" ) type DataStore interface { db.Db - ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) - WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error + ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) + WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error } type UserDataStore struct { @@ -17,16 +19,16 @@ type UserDataStore struct { } // ReadEntry retrieves an entry from the store based on the provided parameters. -func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { +func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := PackKey(typ, []byte(sessionId)) + k := common.PackKey(typ, []byte(sessionId)) return store.Get(ctx, k) } -func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { +func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := PackKey(typ, []byte(sessionId)) + k := common.PackKey(typ, []byte(sessionId)) return store.Put(ctx, k, value) } From c95b97cb148114aed46a8aa84da8ec9faf2dcaee Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 01:45:38 +0000 Subject: [PATCH 190/261] Export user data store --- .../utils/userStore.go => common/user_store.go | 16 +++++++--------- internal/handlers/ussd/menuhandler.go | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) rename internal/utils/userStore.go => common/user_store.go (55%) diff --git a/internal/utils/userStore.go b/common/user_store.go similarity index 55% rename from internal/utils/userStore.go rename to common/user_store.go index 13682b7..8d982ab 100644 --- a/internal/utils/userStore.go +++ b/common/user_store.go @@ -1,17 +1,15 @@ -package utils +package common import ( "context" - "git.grassecon.net/urdt/ussd/common" - "git.defalsify.org/vise.git/db" ) type DataStore interface { db.Db - ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) - WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error + ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) + WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error } type UserDataStore struct { @@ -19,16 +17,16 @@ type UserDataStore struct { } // ReadEntry retrieves an entry from the store based on the provided parameters. -func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) { +func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := common.PackKey(typ, []byte(sessionId)) + k := PackKey(typ, []byte(sessionId)) return store.Get(ctx, k) } -func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error { +func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := common.PackKey(typ, []byte(sessionId)) + k := PackKey(typ, []byte(sessionId)) return store.Put(ctx, k, value) } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index e2bcfaf..303bea9 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -63,7 +63,7 @@ type Handlers struct { pe *persist.Persister st *state.State ca cache.Memory - userdataStore utils.DataStore + userdataStore common.DataStore flagManager *asm.FlagParser accountService server.AccountServiceInterface } @@ -72,7 +72,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } - userDb := &utils.UserDataStore{ + userDb := &common.UserDataStore{ Db: userdataStore, } h := &Handlers{ From adaa0c63ef78486223ec5e9646640599ceda685d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 14:12:42 +0300 Subject: [PATCH 191/261] 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 b404ae95fbfa4fdec0b7c84e7c7486618a754eca Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:30:38 +0300 Subject: [PATCH 192/261] setup an admin store based on fs --- internal/utils/adminstore.go | 111 ++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go index ddd6bd9..43a3bfd 100644 --- a/internal/utils/adminstore.go +++ b/internal/utils/adminstore.go @@ -1,80 +1,85 @@ package utils import ( - "bufio" - "log" + "context" + "encoding/json" "os" - "strconv" - "strings" - "git.grassecon.net/urdt/ussd/initializers" + "git.defalsify.org/vise.git/db" + fsdb "git.defalsify.org/vise.git/db/fs" + "git.defalsify.org/vise.git/logging" ) -type AdminStore struct { - filePath string +var ( + logg = logging.NewVanilla().WithDomain("adminstore") +) + +type Admin struct { + PhoneNumber string `json:"phonenumber"` } -// Creates a new Admin store -func NewAdminStore(filePath string) *AdminStore { - return &AdminStore{filePath: filePath} +type Config struct { + Admins []Admin `json:"admins"` +} + +type AdminStore struct { + ctx context.Context + fsStore db.Db +} + +func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) { + fsStore, err := getFsStore(ctx, fileName) + if err != nil { + return nil, err + } + return &AdminStore{ctx: ctx, fsStore: fsStore}, nil +} + +func getFsStore(ctx context.Context, connectStr string) (db.Db, error) { + fsStore := fsdb.NewFsDb() + err := fsStore.Connect(ctx, connectStr) + fsStore.SetPrefix(db.DATATYPE_USERDATA) + if err != nil { + return nil, err + } + return fsStore, nil } // Seed initializes a list of phonenumbers with admin privileges func (as *AdminStore) Seed() error { - var adminNumbers []int64 + var config Config - numbersEnv := initializers.GetEnv("ADMIN_NUMBERS", "") - for _, numStr := range strings.Split(numbersEnv, ",") { - if num, err := strconv.ParseInt(strings.TrimSpace(numStr), 10, 64); err == nil { - adminNumbers = append(adminNumbers, num) - } else { - log.Printf("Skipping invalid number: %s", numStr) - } - } - file, err := os.Create(as.filePath) + store := as.fsStore + defer store.Close() + + data, err := os.ReadFile("admin_numbers.json") if err != nil { return err } - defer file.Close() - - writer := bufio.NewWriter(file) - for _, num := range adminNumbers { - _, err := writer.WriteString(strconv.FormatInt(num, 10) + "\n") + if err := json.Unmarshal(data, &config); err != nil { + return err + } + for _, admin := range config.Admins { + err := store.Put(as.ctx, []byte(admin.PhoneNumber), []byte("1")) if err != nil { + logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber) return err } } - return writer.Flush() + return nil } -func (as *AdminStore) load() ([]int64, error) { - file, err := os.Open(as.filePath) +// Checks if the given sessionId is listed as an admin. +func (as *AdminStore) IsAdmin(sessionId string) (bool, error) { + _, err := as.fsStore.Get(as.ctx, []byte(sessionId)) if err != nil { - return nil, err - } - defer file.Close() - - var numbers []int64 - scanner := bufio.NewScanner(file) - for scanner.Scan() { - num, err := strconv.ParseInt(scanner.Text(), 10, 64) - if err != nil { - return nil, err - } - numbers = append(numbers, num) - } - return numbers, scanner.Err() -} - -func (as *AdminStore) IsAdmin(phoneNumber int64) (bool, error) { - phoneNumbers, err := as.load() - if err != nil { - return false, err - } - for _, phonenumber := range phoneNumbers { - if phonenumber == phoneNumber { - return true, nil + if db.IsNotFound(err) { + logg.Printf(logging.LVL_INFO, "Returning false because session id was not found") + return false, nil + } else { + return false, err } } - return false, nil + + return true, nil } From c42b1cd66b9dd63b5f31ca5c60bc483d2230bee1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:32:01 +0300 Subject: [PATCH 193/261] provide required handler functions and admin store --- internal/handlers/handlerservice.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index b4cd592..247e6c5 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -8,6 +8,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" + "git.grassecon.net/urdt/ussd/internal/utils" ) type HandlerService interface { @@ -28,6 +29,7 @@ type LocalHandlerService struct { DbRs *resource.DbResource Pe *persist.Persister UserdataStore *db.Db + AdminStore *utils.AdminStore Cfg engine.Config Rs resource.Resource } @@ -53,8 +55,12 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } +func (ls *LocalHandlerService) SetAdminStore(adminstore *utils.AdminStore) { + ls.AdminStore = adminstore +} + func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, accountService) + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) if err != nil { return nil, err } @@ -103,6 +109,8 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) + ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin) + ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin) return ussdHandlers, nil } From c2068db050a931b6f6b9e75bdf876f612d072087 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:33:22 +0300 Subject: [PATCH 194/261] update handler functions --- services/registration/confirm_others_new_pin.vis | 4 ++-- services/registration/enter_others_new_pin.vis | 4 ++-- services/registration/pin_reset_result.vis | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis index be6734f..319346a 100644 --- a/services/registration/confirm_others_new_pin.vis +++ b/services/registration/confirm_others_new_pin.vis @@ -1,9 +1,9 @@ LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number CATCH invalid_others_pin flag_valid_pin 0 -LOAD save_temporary_pin 6 -RELOAD save_temporary_pin CATCH pin_reset_result flag_account_authorized 1 +LOAD save_others_temporary_pin 6 +RELOAD save_others_temporary_pin MOUT back 0 HALT INCMP _ 0 diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index aa2818d..16db41c 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -2,7 +2,7 @@ LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 HALT -INCMP _ 0 -LOAD verify_new_pin 0 +LOAD verify_new_pin 6 RELOAD verify_new_pin +INCMP _ 0 INCMP * confirm_others_new_pin diff --git a/services/registration/pin_reset_result.vis b/services/registration/pin_reset_result.vis index ed71200..34b9789 100644 --- a/services/registration/pin_reset_result.vis +++ b/services/registration/pin_reset_result.vis @@ -1,5 +1,6 @@ LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number +LOAD reset_others_pin 6 MOUT back 0 MOUT quit 9 HALT From dc418771a79dbb8653fc3251554b0d1591574d4d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:33:48 +0300 Subject: [PATCH 195/261] attach an admin store for phone numbers --- cmd/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 4d23011..7dd3cd6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -49,7 +49,7 @@ func main() { ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") - as := utils.NewAdminStore("admin_numbers.txt") + as, _ := utils.NewAdminStore(ctx, "admin_numbers") as.Seed() cfg := engine.Config{ @@ -94,6 +94,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdatastore) + lhs.SetAdminStore(as) lhs.SetPersister(pe) if err != nil { From 017691a40cac3bab37121d875a3da914eca8bd53 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:41:14 +0300 Subject: [PATCH 196/261] define structure for admin numbers --- admin_numbers.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 admin_numbers.json diff --git a/admin_numbers.json b/admin_numbers.json new file mode 100644 index 0000000..ca58a23 --- /dev/null +++ b/admin_numbers.json @@ -0,0 +1,7 @@ +{ + "admins": [ + { + "phonenumber" : "" + } + ] +} \ No newline at end of file From 888d3befe99437be89ade4cd60915ca3730899c4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:50:12 +0300 Subject: [PATCH 197/261] add actual pin reset functionality --- internal/handlers/ussd/menuhandler.go | 61 ++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 70510ee..8b0c385 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -69,11 +69,12 @@ type Handlers struct { st *state.State ca cache.Memory userdataStore utils.DataStore + adminstore *utils.AdminStore flagManager *asm.FlagParser accountService server.AccountServiceInterface } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService server.AccountServiceInterface) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } @@ -83,6 +84,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s h := &Handlers{ userdataStore: userDb, flagManager: appFlags, + adminstore: adminstore, accountService: accountService, } return h, nil @@ -120,9 +122,7 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource sessionId, _ := ctx.Value("SessionId").(string) flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") - number, _ := strconv.ParseInt(sessionId, 10, 64) - as := utils.NewAdminStore("admin_numbers.txt") - isAdmin, _ := as.IsAdmin(number) + isAdmin, _ := h.adminstore.IsAdmin(sessionId) if isAdmin { r.FlagSet = append(r.FlagSet, flag_admin_privilege) @@ -220,7 +220,11 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + if err != nil { + return res, err + } + temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN) if err != nil { return res, err } @@ -272,6 +276,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt } res.FlagReset = append(res.FlagReset, flag_incorrect_pin) + fmt.Println("Saving:", string(accountPIN)) store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) @@ -282,6 +287,29 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt return res, nil } +func (h *Handlers) SaveOthersTemporaryPin(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") + } + temporaryPin := string(input) + blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + + if err != nil { + return res, err + } + err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN, []byte(temporaryPin)) + if err != nil { + return res, err + } + + 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) @@ -672,7 +700,6 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by var res resource.Result flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") - res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) return res, nil } @@ -755,6 +782,28 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, nil } +func (h *Handlers) ResetOthersPin(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") + } + blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + if err != nil { + return res, err + } + temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_PIN) + if err != nil { + return res, err + } + err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + if err != nil { + return res, nil + } + return res, nil +} + func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") From cf523e30f89571ed58c933bbd2b0162034947cdc Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:50:31 +0300 Subject: [PATCH 198/261] update handler in test --- internal/handlers/ussd/menuhandler_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 28d25e8..d23408a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -39,7 +39,7 @@ func TestNewHandlers(t *testing.T) { } t.Run("Valid UserDataStore", func(t *testing.T) { mockStore := &mocks.MockUserDataStore{} - handlers, err := NewHandlers(fm.parser, mockStore, &accountService) + handlers, err := NewHandlers(fm.parser, mockStore, nil, &accountService) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -55,7 +55,7 @@ func TestNewHandlers(t *testing.T) { t.Run("Nil UserDataStore", func(t *testing.T) { appFlags := &asm.FlagParser{} - handlers, err := NewHandlers(appFlags, nil, &accountService) + handlers, err := NewHandlers(appFlags, nil, nil, &accountService) if err == nil { t.Fatal("expected an error, got none") @@ -1481,8 +1481,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}, From 14648fec6cfcc348bf5e38ff6e0eb17a53cc015e Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 12:02:16 +0000 Subject: [PATCH 199/261] Edit comment --- common/user_store.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/user_store.go b/common/user_store.go index 8d982ab..4b8409f 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -16,7 +16,7 @@ type UserDataStore struct { db.Db } -// ReadEntry retrieves an entry from the store based on the provided parameters. +// ReadEntry retrieves an entry to the userdata store. func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) @@ -24,6 +24,7 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ return store.Get(ctx, k) } +// WriteEntry adds an entry to the userdata store. func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) From ff26ccc545d1b09456a1875d8e59ef290a15f349 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 13:09:15 +0000 Subject: [PATCH 200/261] Expose api interface --- cmd/africastalking/main.go | 4 ++-- cmd/async/main.go | 4 ++-- cmd/http/main.go | 4 ++-- cmd/main.go | 4 ++-- internal/handlers/handlerservice.go | 5 +++-- internal/handlers/ussd/menuhandler.go | 6 +++--- internal/testutil/TestEngine.go | 6 +++--- internal/testutil/testtag/offlinetest.go | 4 ++-- {internal/handlers/server => remote}/accountservice.go | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) rename {internal/handlers/server => remote}/accountservice.go (99%) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 79fb091..7c98f38 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -19,9 +19,9 @@ import ( "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/handlers/server" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -139,7 +139,7 @@ func main() { os.Exit(1) } - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/cmd/async/main.go b/cmd/async/main.go index 902dfc7..b879c68 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -16,8 +16,8 @@ import ( "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/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -106,7 +106,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/http/main.go b/cmd/http/main.go index ece882e..638ca87 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -18,9 +18,9 @@ import ( "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/handlers/server" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -99,7 +99,7 @@ func main() { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/cmd/main.go b/cmd/main.go index 21ca0c3..13ceb3c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,8 +13,8 @@ import ( "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/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -97,7 +97,7 @@ func main() { os.Exit(1) } - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 311e694..b409fa6 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -6,8 +6,9 @@ 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" + "git.grassecon.net/urdt/ussd/remote" ) type HandlerService interface { @@ -53,7 +54,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } -func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { +func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) if err != nil { return nil, err diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 303bea9..aa5c1aa 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/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/common" + "git.grassecon.net/urdt/ussd/remote" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -65,10 +65,10 @@ type Handlers struct { ca cache.Memory userdataStore common.DataStore flagManager *asm.FlagParser - accountService server.AccountServiceInterface + accountService remote.AccountServiceInterface } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService remote.AccountServiceInterface) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index bd6bc7f..fa585ae 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -11,11 +11,11 @@ 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" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "git.grassecon.net/urdt/ussd/internal/testutil/testtag" testdataloader "github.com/peteole/testdata-loader" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -83,7 +83,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { } if testtag.AccountService == nil { - testtag.AccountService = &server.AccountService{} + testtag.AccountService = &remote.AccountService{} } switch testtag.AccountService.(type) { @@ -91,7 +91,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { go func() { eventChannel <- false }() - case *server.AccountService: + case *remote.AccountService: go func() { time.Sleep(5 * time.Second) // Wait for 5 seconds eventChannel <- true diff --git a/internal/testutil/testtag/offlinetest.go b/internal/testutil/testtag/offlinetest.go index 70e2e80..831bf09 100644 --- a/internal/testutil/testtag/offlinetest.go +++ b/internal/testutil/testtag/offlinetest.go @@ -3,10 +3,10 @@ package testtag import ( - "git.grassecon.net/urdt/ussd/internal/handlers/server" + "git.grassecon.net/urdt/ussd/remote" accountservice "git.grassecon.net/urdt/ussd/internal/testutil/testservice" ) var ( - AccountService server.AccountServiceInterface = &accountservice.TestAccountService{} + AccountService remote.AccountServiceInterface = &accountservice.TestAccountService{} ) diff --git a/internal/handlers/server/accountservice.go b/remote/accountservice.go similarity index 99% rename from internal/handlers/server/accountservice.go rename to remote/accountservice.go index 890d1db..236ccf4 100644 --- a/internal/handlers/server/accountservice.go +++ b/remote/accountservice.go @@ -1,4 +1,4 @@ -package server +package remote import ( "context" From d93a26f9b0d2ae02c9798825efac879d09630cf0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 17:58:52 +0300 Subject: [PATCH 201/261] remove function exec after HALT --- services/registration/enter_other_number.vis | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index e2fa243..0957165 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -4,7 +4,4 @@ RELOAD reset_account_authorized MOUT back 0 HALT INCMP _ 0 -LOAD validate_blocked_number 20 -RELOAD validate_blocked_number -CATCH unregistered_number flag_unregistered_number 1 INCMP enter_others_new_pin * From 41585f831c3e38978c99d34a20859e4ab8ef6e71 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 18:00:38 +0300 Subject: [PATCH 202/261] move catch and load to next node --- services/registration/enter_others_new_pin.vis | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index 16db41c..49c2426 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,3 +1,6 @@ +LOAD validate_blocked_number 20 +RELOAD validate_blocked_number +CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 From 7c823e07ca6c4f0175e58902522f1de97a104599 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 18:01:20 +0300 Subject: [PATCH 203/261] move to root node after on back --- services/registration/unregistered_number.vis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis index 114113f..0c66912 100644 --- a/services/registration/unregistered_number.vis +++ b/services/registration/unregistered_number.vis @@ -1,7 +1,7 @@ LOAD reset_unregistered_number 0 RELOAD reset_unregistered_number -MOUT retry 1 +MOUT back 1 MOUT quit 9 HALT -INCMP _ 1 +INCMP ^ 1 INCMP quit 9 From ea4c6d9314158ecb7a6625b9c6544285da4a7bd1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 18:01:43 +0300 Subject: [PATCH 204/261] check for phone number validity --- internal/handlers/ussd/menuhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8b0c385..4d6f724 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -816,7 +816,6 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input var err error flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") - store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -825,6 +824,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input blockedNumber := string(input) _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) if !isValidPhoneNumber(blockedNumber) { + res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil } if err != nil { From 66b34eaea4f83147e6f4e3296636eee0bf21e812 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 30 Oct 2024 18:21:49 +0300 Subject: [PATCH 205/261] 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 206/261] 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 207/261] 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 833d52a558b2e20359ebe4beeb482dd7e2757397 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 21:26:11 +0300 Subject: [PATCH 208/261] remove print message --- internal/handlers/ussd/menuhandler.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index bbcfd58..3ffa61b 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" @@ -283,10 +283,7 @@ 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) - fmt.Println("Saving:", string(accountPIN)) - store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) if err != nil { From 8093eae61aeb35e0a76fce275fca04fb950adedb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 21:26:52 +0300 Subject: [PATCH 209/261] use 0 instead of 1 for back --- services/registration/unregistered_number.vis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis index 0c66912..0ff96be 100644 --- a/services/registration/unregistered_number.vis +++ b/services/registration/unregistered_number.vis @@ -1,7 +1,7 @@ LOAD reset_unregistered_number 0 RELOAD reset_unregistered_number -MOUT back 1 +MOUT back 0 MOUT quit 9 HALT -INCMP ^ 1 +INCMP ^ 0 INCMP quit 9 From c6ca3f6be4fb405ebf0a620a26adc677242d8d8f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 22:32:38 +0300 Subject: [PATCH 210/261] fix sink error --- services/registration/confirm_others_new_pin.vis | 2 +- services/registration/enter_others_new_pin.vis | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis index 319346a..f63d7ae 100644 --- a/services/registration/confirm_others_new_pin.vis +++ b/services/registration/confirm_others_new_pin.vis @@ -1,4 +1,4 @@ -LOAD retrieve_blocked_number 0 +RELOAD retrieve_blocked_number MAP retrieve_blocked_number CATCH invalid_others_pin flag_valid_pin 0 CATCH pin_reset_result flag_account_authorized 1 diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index 49c2426..7711c97 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,7 +1,8 @@ -LOAD validate_blocked_number 20 +LOAD validate_blocked_number 6 RELOAD validate_blocked_number CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 +RELOAD retrieve_blocked_number MAP retrieve_blocked_number MOUT back 0 HALT From d4341940218dc1b24795ccdb10165fc3098242a0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 23:21:15 +0300 Subject: [PATCH 211/261] catch incorrect pin entry --- services/registration/confirm_others_new_pin.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis index f63d7ae..9132dc4 100644 --- a/services/registration/confirm_others_new_pin.vis +++ b/services/registration/confirm_others_new_pin.vis @@ -1,3 +1,4 @@ +CATCH pin_entry flag_incorrect_pin 1 RELOAD retrieve_blocked_number MAP retrieve_blocked_number CATCH invalid_others_pin flag_valid_pin 0 From 1e638238ed368319fb84ed9deb8d90d0df55b28d Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 01:28:37 +0000 Subject: [PATCH 212/261] WIP refactor models usage --- config/config.go | 67 ++++++-- internal/handlers/ussd/menuhandler.go | 20 +-- internal/handlers/ussd/menuhandler_test.go | 1 - internal/models/accountresponse.go | 10 +- internal/models/balanceresponse.go | 17 +- internal/models/trackstatusresponse.go | 23 ++- internal/models/vouchersresponse.go | 13 -- internal/testutil/mocks/servicemock.go | 27 ++-- .../testservice/TestAccountService.go | 78 +++------ remote/accountservice.go | 149 ++++++++++++------ 10 files changed, 229 insertions(+), 176 deletions(-) diff --git a/config/config.go b/config/config.go index 43466c3..a5c89a7 100644 --- a/config/config.go +++ b/config/config.go @@ -1,18 +1,67 @@ package config -import "git.grassecon.net/urdt/ussd/initializers" +import ( + "net/url" + + "git.grassecon.net/urdt/ussd/initializers" +) + +const ( + createAccountPath = "/api/v2/account/create" + trackStatusPath = "/api/track" + balancePathPrefix = "/api/account" + trackPath = "/api/v2/account/status" + voucherHoldingsPathPrefix = "/api/v1/holdings" + voucherTransfersPathPrefix = "/api/v1/transfers/last10" +) + +var ( + custodialURLBase string + dataURLBase string + CustodialAPIKey string + DataAPIKey string +) var ( CreateAccountURL string TrackStatusURL string - BalanceURL string - TrackURL string + BalanceURL string + TrackURL string + VoucherHoldingsURL string + VoucherTransfersURL string ) -// 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") - 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") +func setBase() error { + var err error + + custodialURLBase = initializers.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003") + dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006") + CustodialAPIKey = initializers.GetEnv("CUSTODIAL_API_KEY", "xd") + DataAPIKey = initializers.GetEnv("DATA_API_KEY", "xd") + + _, err = url.JoinPath(custodialURLBase, "/foo") + if err != nil { + return err + } + _, err = url.JoinPath(dataURLBase, "/bar") + if err != nil { + return err + } + return nil +} + +// LoadConfig initializes the configuration values after environment variables are loaded. +func LoadConfig() error { + err := setBase() + if err != nil { + return err + } + CreateAccountURL, _ = url.JoinPath(custodialURLBase, createAccountPath) + TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath) + BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix) + TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) + VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) + VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) + + return nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f568e6a..5f6f81a 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -149,12 +149,12 @@ 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") - okResponse, err := h.accountService.CreateAccount(ctx) + r, err := h.accountService.CreateAccount(ctx) if err != nil { return err } - trackingId := okResponse.Result["trackingId"].(string) - publicKey := okResponse.Result["publicKey"].(string) + trackingId := r.TrackingId + publicKey := r.PublicKey data := map[common.DataTyp]string{ common.DATA_TRACKING_ID: trackingId, @@ -682,6 +682,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( 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) @@ -699,14 +700,13 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input balanceResponse, err := h.accountService.CheckBalance(ctx, 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 + + //balance := balanceResponse.Result.Balance + balance := balanceResponse.Balance switch balanceType { case "my": @@ -1066,13 +1066,13 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by } // Return if there is no voucher - if len(vouchersResp.Result.Holdings) == 0 { + if len(vouchersResp) == 0 { res.FlagSet = append(res.FlagSet, flag_no_active_voucher) return res, nil } // Use only the first voucher - firstVoucher := vouchersResp.Result.Holdings[0] + firstVoucher := vouchersResp[0] defaultSym := firstVoucher.TokenSymbol defaultBal := firstVoucher.Balance @@ -1119,7 +1119,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - data := utils.ProcessVouchers(vouchersResp.Result.Holdings) + data := utils.ProcessVouchers(vouchersResp) // Store all voucher data dataMap := map[string]string{ diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index bae95a8..5f3c953 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1159,7 +1159,6 @@ func TestCheckAccountStatus(t *testing.T) { // Assert that expectations were met mockDataStore.AssertExpectations(t) - }) } diff --git a/internal/models/accountresponse.go b/internal/models/accountresponse.go index efcc30b..278e0e9 100644 --- a/internal/models/accountresponse.go +++ b/internal/models/accountresponse.go @@ -1,10 +1,6 @@ package models -type AccountResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` // Include the description field - Result struct { - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` - } `json:"result"` +type AccountResult struct { + PublicKey string `json:"publicKey"` + TrackingId string `json:"trackingId"` } diff --git a/internal/models/balanceresponse.go b/internal/models/balanceresponse.go index 57c8e5a..3d57c95 100644 --- a/internal/models/balanceresponse.go +++ b/internal/models/balanceresponse.go @@ -3,10 +3,15 @@ package models import "encoding/json" -type BalanceResponse struct { - Ok bool `json:"ok"` - Result struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - } `json:"result"` +//type BalanceResponse struct { +// Ok bool `json:"ok"` +// Result struct { +// Balance string `json:"balance"` +// Nonce json.Number `json:"nonce"` +// } `json:"result"` +//} +// +type BalanceResult struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` } diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 6b96d90..6130c6e 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -4,23 +4,18 @@ import ( "encoding/json" "time" ) -type Transaction struct { +//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 TrackStatusResult 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"` - 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"` - } - } `json:"result"` -} diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go index 09b085d..2640e7f 100644 --- a/internal/models/vouchersresponse.go +++ b/internal/models/vouchersresponse.go @@ -1,14 +1 @@ package models - -import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - -type VoucherHoldingResponse struct { - 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/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index de0e99a..3b36ba7 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -5,6 +5,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" "github.com/stretchr/testify/mock" ) @@ -13,28 +14,28 @@ type MockAccountService struct { mock.Mock } -func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { +func (m *MockAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { args := m.Called() - return args.Get(0).(*api.OKResponse), args.Error(1) + return args.Get(0).(*models.AccountResult), args.Error(1) } -func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { +func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { args := m.Called(publicKey) - return args.Get(0).(*models.BalanceResponse), args.Error(1) + return args.Get(0).(*models.BalanceResult), args.Error(1) } -func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { +func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*api.OKResponse, error) { args := m.Called(trackingId) - return args.Get(0).(*models.TrackStatusResponse), args.Error(1) -} - -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) } - -func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { +func (m *MockAccountService) CheckAccountStatus(ctx context.Context,publicKey string) (*models.TrackStatusResult, error) { args := m.Called(publicKey) - return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) + return args.Get(0).(*models.TrackStatusResult), args.Error(1) +} + + +func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { + args := m.Called(publicKey) + return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) } diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 745b80d..885e63f 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -13,54 +13,29 @@ import ( type TestAccountService struct { } -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", - }, +func (tas *TestAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { + return &models.AccountResult { + TrackingId: "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", + PublicKey: "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", }, 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"), - }, +func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { + balanceResponse := &models.BalanceResult { + Balance: "0.003 CELO", + Nonce: json.Number("0"), } - return balanceResponse, 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) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { + return &models.TrackStatusResult { + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", + }, nil } func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { @@ -73,18 +48,13 @@ 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: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{ - { - ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", - TokenSymbol: "SRF", - TokenDecimals: "6", - Balance: "2745987", - }, - }, +func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { + return []dataserviceapi.TokenHoldings { + dataserviceapi.TokenHoldings { + ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", + TokenSymbol: "SRF", + TokenDecimals: "6", + Balance: "2745987", }, - }, nil + }, nil } diff --git a/remote/accountservice.go b/remote/accountservice.go index 236ccf4..c11910f 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -4,27 +4,29 @@ import ( "context" "encoding/json" "errors" - "fmt" "io" "net/http" - "os" + "net/url" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" + "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" - "github.com/grassrootseconomics/eth-custodial/pkg/api" ) var ( - okResponse api.OKResponse - errResponse api.ErrResponse ) type AccountServiceInterface interface { - CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) - CreateAccount(ctx context.Context) (*api.OKResponse, error) - CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) + //CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) + CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) + CreateAccount(ctx context.Context) (*models.AccountResult, error) + //CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) + // TODO: poorly named method seems to be checking transaction + CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) - FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) + FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) + //FetchVouchers(ctx context.Context, publicKey string) (*api.OKResponse, error) } type AccountService struct { @@ -39,32 +41,38 @@ type AccountService 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(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { - resp, err := http.Get(config.BalanceURL + trackingId) - if err != nil { - return nil, err - } - defer resp.Body.Close() +func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { + var r models.TrackStatusResult - body, err := io.ReadAll(resp.Body) + ep, err := url.JoinPath(config.TrackURL, trackingId) if err != nil { return nil, err } - var trackResp models.TrackStatusResponse - err = json.Unmarshal(body, &trackResp) + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } - return &trackResp, nil + _, err = doCustodialRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil } func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { + var okResponse api.OKResponse + var errResponse api.ErrResponse 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) + + ep, err := url.JoinPath(config.TrackURL, publicKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } @@ -104,41 +112,83 @@ func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey stri // 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(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { - resp, err := http.Get(config.BalanceURL + publicKey) +func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { + var balanceResult models.BalanceResult + + ep, err := url.JoinPath(config.BalanceURL, publicKey) if err != nil { return nil, err } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } - var balanceResp models.BalanceResponse - err = json.Unmarshal(body, &balanceResp) - if err != nil { - return nil, err - } - return &balanceResp, nil + + _, err = doCustodialRequest(ctx, req, &balanceResult) + return &balanceResult, err } + // 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. // - 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(ctx context.Context) (*api.OKResponse, error) { - var err error - +func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { + var r models.AccountResult // Create a new request req, err := http.NewRequest("POST", config.CreateAccountURL, nil) if err != nil { return nil, err } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-GE-KEY", "xd") + _, err = doCustodialRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil +} + +// 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(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { + var r []dataserviceapi.TokenHoldings + + req, err := http.NewRequest("GET", config.VoucherHoldingsURL, nil) + if err != nil { + return nil, err + } + + _, err = doDataRequest(ctx, req, r) + if err != nil { + return nil, err + } + + return r, nil + +// +// file, err := os.Open("sample_tokens.json") +// if err != nil { +// return nil, err +// } +// defer file.Close() +// var holdings models.VoucherHoldingResponse +// +// if err := json.NewDecoder(file).Decode(&holdings); err != nil { +// return nil, err +// } +// return &holdings, nil +} + +func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { + var okResponse api.OKResponse + var errResponse api.ErrResponse + + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { errResponse.Description = err.Error() @@ -165,21 +215,22 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, e return nil, errors.New("Empty api result") } return &okResponse, nil -} -// 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(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { - file, err := os.Open("sample_tokens.json") + v, err := json.Marshal(okResponse.Result) if err != nil { return nil, err } - defer file.Close() - var holdings models.VoucherHoldingResponse - if err := json.NewDecoder(file).Decode(&holdings); err != nil { - return nil, err - } - return &holdings, nil + err = json.Unmarshal(v, &rcpt) + return &okResponse, err +} + +func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { + req.Header.Set("X-GE-KEY", config.CustodialAPIKey) + return doRequest(ctx, req, rcpt) +} + +func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { + req.Header.Set("X-GE-KEY", config.DataAPIKey) + return doRequest(ctx, req, rcpt) } From a48170321c03aa232aabf7065f98c08c947e6e9f Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 01:51:36 +0000 Subject: [PATCH 213/261] Finish refactor result models --- internal/handlers/ussd/menuhandler_test.go | 136 ++++++--------------- 1 file changed, 36 insertions(+), 100 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 5f3c953..77447ca 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -88,18 +88,14 @@ func TestCreateAccount(t *testing.T) { tests := []struct { name string - serverResponse *api.OKResponse + serverResponse *models.AccountResult expectedResult resource.Result }{ { name: "Test account creation success", - serverResponse: &api.OKResponse{ - Ok: true, - Description: "Account creation successed", - Result: map[string]any{ - "trackingId": "1234567890", - "publicKey": "0xD3adB33f", - }, + serverResponse: &models.AccountResult{ + TrackingId: "1234567890", + PublicKey: "0xD3adB33f", }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_created}, @@ -119,9 +115,9 @@ func TestCreateAccount(t *testing.T) { flagManager: fm.parser, } - publicKey := tt.serverResponse.Result["publicKey"].(string) + publicKey := tt.serverResponse.PublicKey data := map[common.DataTyp]string{ - common.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), + common.DATA_TRACKING_ID: tt.serverResponse.TrackingId, common.DATA_PUBLIC_KEY: publicKey, } @@ -1055,7 +1051,7 @@ func TestCheckAccountStatus(t *testing.T) { name string input []byte serverResponse *api.OKResponse - response *models.TrackStatusResponse + response *models.TrackStatusResult expectedResult resource.Result }{ { @@ -1068,25 +1064,12 @@ func TestCheckAccountStatus(t *testing.T) { "active": true, }, }, - 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: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, + response: &models.TrackStatusResult { + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_success}, @@ -1096,25 +1079,12 @@ func TestCheckAccountStatus(t *testing.T) { { name: "Test when the account is not yet on the sarafu network", 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: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, + response: &models.TrackStatusResult{ + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", }, serverResponse: &api.OKResponse{ Ok: true, @@ -1140,7 +1110,7 @@ func TestCheckAccountStatus(t *testing.T) { flagManager: fm.parser, } - status := tt.response.Result.Transaction.Status + status := tt.response.Status // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) @@ -1833,36 +1803,14 @@ func TestFetchCustodialBalances(t *testing.T) { tests := []struct { name string - balanceResonse *models.BalanceResponse + balanceResponse *models.BalanceResult 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"), - }, + balanceResponse: &models.BalanceResult{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), }, expectedResult: resource.Result{ FlagReset: []uint32{flag_api_error}, @@ -1886,7 +1834,7 @@ func TestFetchCustodialBalances(t *testing.T) { // Set up the expected behavior of the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) + mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResponse, nil) // Call the method res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte("")) @@ -1918,35 +1866,24 @@ func TestSetDefaultVoucher(t *testing.T) { tests := []struct { name string - vouchersResp *models.VoucherHoldingResponse + vouchersResp []dataserviceapi.TokenHoldings expectedResult resource.Result }{ { name: "Test set default voucher when no active voucher exists", - vouchersResp: &models.VoucherHoldingResponse{ - Ok: true, - Description: "Vouchers fetched successfully", - Result: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{ - { - ContractAddress: "0x123", - TokenSymbol: "TOKEN1", - TokenDecimals: "18", - Balance: "100", - }, - }, + vouchersResp: []dataserviceapi.TokenHoldings { + dataserviceapi.TokenHoldings{ + 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: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{}, - }, + vouchersResp: []dataserviceapi.TokenHoldings { }, expectedResult: resource.Result{ FlagSet: []uint32{flag_no_active_voucher}, @@ -1970,8 +1907,8 @@ func TestSetDefaultVoucher(t *testing.T) { mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) - if len(tt.vouchersResp.Result.Holdings) > 0 { - firstVoucher := tt.vouchersResp.Result.Holdings[0] + if len(tt.vouchersResp) > 0 { + firstVoucher := tt.vouchersResp[0] mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(firstVoucher.TokenSymbol)).Return(nil) mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(firstVoucher.Balance)).Return(nil) } @@ -2006,8 +1943,7 @@ func TestCheckVouchers(t *testing.T) { mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockVouchersResponse := &models.VoucherHoldingResponse{} - mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{ + mockVouchersResponse := []dataserviceapi.TokenHoldings{ {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } From dc198215b1272f5f745babe3e3319295572fac3c Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 02:03:29 +0000 Subject: [PATCH 214/261] Remove commented code --- internal/handlers/ussd/menuhandler.go | 1 - internal/handlers/ussd/menuhandler_test.go | 1 - internal/models/balanceresponse.go | 8 -------- internal/models/trackstatusresponse.go | 7 ------- internal/models/vouchersresponse.go | 1 - remote/accountservice.go | 16 ---------------- 6 files changed, 34 deletions(-) delete mode 100644 internal/models/vouchersresponse.go diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 5f6f81a..98a633c 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -705,7 +705,6 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input } res.FlagReset = append(res.FlagReset, flag_api_error) - //balance := balanceResponse.Result.Balance balance := balanceResponse.Balance switch balanceType { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 77447ca..8708145 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -815,7 +815,6 @@ func TestAuthorize(t *testing.T) { // Create required mocks mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) - //expectedResult := resource.Result{} mockState := state.NewState(16) flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") diff --git a/internal/models/balanceresponse.go b/internal/models/balanceresponse.go index 3d57c95..b2baa41 100644 --- a/internal/models/balanceresponse.go +++ b/internal/models/balanceresponse.go @@ -3,14 +3,6 @@ package models import "encoding/json" -//type BalanceResponse struct { -// Ok bool `json:"ok"` -// Result struct { -// Balance string `json:"balance"` -// Nonce json.Number `json:"nonce"` -// } `json:"result"` -//} -// type BalanceResult struct { Balance string `json:"balance"` Nonce json.Number `json:"nonce"` diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 6130c6e..202b69b 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -4,13 +4,6 @@ import ( "encoding/json" "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 TrackStatusResult struct { CreatedAt time.Time `json:"createdAt"` diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go deleted file mode 100644 index 2640e7f..0000000 --- a/internal/models/vouchersresponse.go +++ /dev/null @@ -1 +0,0 @@ -package models diff --git a/remote/accountservice.go b/remote/accountservice.go index c11910f..bc98cf1 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -18,15 +18,12 @@ var ( ) type AccountServiceInterface interface { - //CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) CreateAccount(ctx context.Context) (*models.AccountResult, error) - //CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) // TODO: poorly named method seems to be checking transaction CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) - //FetchVouchers(ctx context.Context, publicKey string) (*api.OKResponse, error) } type AccountService struct { @@ -169,19 +166,6 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( } return r, nil - -// -// file, err := os.Open("sample_tokens.json") -// if err != nil { -// return nil, err -// } -// defer file.Close() -// var holdings models.VoucherHoldingResponse -// -// if err := json.NewDecoder(file).Decode(&holdings); err != nil { -// return nil, err -// } -// return &holdings, nil } func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { From c4078c528005012a6f3053ff55a6e7cbbc108c0b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 09:21:46 +0300 Subject: [PATCH 215/261] remove extra spaces --- services/registration/confirm_others_new_pin | 2 +- services/registration/pin_reset_success.vis | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/services/registration/confirm_others_new_pin b/services/registration/confirm_others_new_pin index 46c3275..d345a0a 100644 --- a/services/registration/confirm_others_new_pin +++ b/services/registration/confirm_others_new_pin @@ -1 +1 @@ -Please confirm new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file +Please confirm new PIN for:{{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/pin_reset_success.vis b/services/registration/pin_reset_success.vis index c942519..96dee73 100644 --- a/services/registration/pin_reset_success.vis +++ b/services/registration/pin_reset_success.vis @@ -6,5 +6,3 @@ MOUT quit 9 HALT INCMP main 0 INCMP quit 9 - - From 767a3cd64c700b59c1a54b42c57df7c0a1dd6c2b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 09:38:51 +0300 Subject: [PATCH 216/261] remove pin guard menu option --- services/registration/guard_pin_menu | 1 - services/registration/guard_pin_menu_swa | 1 - 2 files changed, 2 deletions(-) delete mode 100644 services/registration/guard_pin_menu delete mode 100644 services/registration/guard_pin_menu_swa diff --git a/services/registration/guard_pin_menu b/services/registration/guard_pin_menu deleted file mode 100644 index 9de0ba0..0000000 --- a/services/registration/guard_pin_menu +++ /dev/null @@ -1 +0,0 @@ -Guard my PIN \ No newline at end of file diff --git a/services/registration/guard_pin_menu_swa b/services/registration/guard_pin_menu_swa deleted file mode 100644 index c6b126f..0000000 --- a/services/registration/guard_pin_menu_swa +++ /dev/null @@ -1 +0,0 @@ -Linda PIN yangu \ No newline at end of file From bab3f673ebb028d4ad2c174aa7f86a6721b80067 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 14:13:21 +0300 Subject: [PATCH 217/261] Removed unused model --- internal/models/tokenresponse.go | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 internal/models/tokenresponse.go diff --git a/internal/models/tokenresponse.go b/internal/models/tokenresponse.go deleted file mode 100644 index d243d93..0000000 --- a/internal/models/tokenresponse.go +++ /dev/null @@ -1,18 +0,0 @@ -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 05ed236e03b48de4500fd3fd482ad42c60aa65ae Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 14:14:10 +0300 Subject: [PATCH 218/261] 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 219/261] 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 220/261] 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 221/261] 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 222/261] 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 223/261] 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 b9c56b04ce3f1eee05cfcc20a55eb38ee769a4ab Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 11:43:27 +0000 Subject: [PATCH 224/261] Remove obsolete track account status code --- internal/handlers/ussd/menuhandler_test.go | 17 ++++------------- internal/models/trackstatusresponse.go | 6 +++++- .../testutil/testservice/TestAccountService.go | 7 +------ 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 8708145..8c3867a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -7,7 +7,6 @@ import ( "log" "path" "testing" - "time" "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/db" @@ -1064,11 +1063,7 @@ func TestCheckAccountStatus(t *testing.T) { }, }, response: &models.TrackStatusResult { - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", + Active: true, }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_success}, @@ -1079,11 +1074,7 @@ func TestCheckAccountStatus(t *testing.T) { name: "Test when the account is not yet on the sarafu network", input: []byte("TrackingId1234"), response: &models.TrackStatusResult{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", + Active: false, }, serverResponse: &api.OKResponse{ Ok: true, @@ -1109,13 +1100,13 @@ func TestCheckAccountStatus(t *testing.T) { flagManager: fm.parser, } - status := tt.response.Status + status := tt.response.Active // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.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, common.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, status).Return(nil).Maybe() // Call the method under test res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 202b69b..47d757d 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -5,10 +5,14 @@ import ( "time" ) -type TrackStatusResult struct { +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 TrackStatusResult struct { + Active bool `json:"active"` +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 885e63f..78a2798 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -3,7 +3,6 @@ package testservice import ( "context" "encoding/json" - "time" "git.grassecon.net/urdt/ussd/internal/models" "github.com/grassrootseconomics/eth-custodial/pkg/api" @@ -30,11 +29,7 @@ func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey strin func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { return &models.TrackStatusResult { - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", + Active: true, }, nil } From e6a369dcddea338d04717babc1503fde10181368 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 14:44:42 +0300 Subject: [PATCH 225/261] 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 b2d180e8ebc0d353a6939d78e8f965fa6b405b89 Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 12:15:07 +0000 Subject: [PATCH 226/261] WIP Trying to clean up account status check --- internal/handlers/ussd/menuhandler.go | 5 +- internal/handlers/ussd/menuhandler_test.go | 35 +++--------- internal/testutil/mocks/servicemock.go | 8 +-- .../testservice/TestAccountService.go | 13 +---- remote/accountservice.go | 55 +------------------ 5 files changed, 15 insertions(+), 101 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 98a633c..ed5f466 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -557,17 +557,16 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b if err != nil { return res, err } - okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) + r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err } res.FlagReset = append(res.FlagReset, flag_api_error) - isActive := okResponse.Result["active"].(bool) if !ok { return res, err } - if isActive { + if r.Active { res.FlagSet = append(res.FlagSet, flag_account_success) res.FlagReset = append(res.FlagReset, flag_account_pending) } else { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 8c3867a..7940704 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1053,36 +1053,18 @@ func TestCheckAccountStatus(t *testing.T) { expectedResult resource.Result }{ { - name: "Test when account is on the Sarafu network", - input: []byte("TrackingId1234"), - serverResponse: &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": true, - }, - }, - response: &models.TrackStatusResult { - Active: true, - }, - expectedResult: resource.Result{ - FlagSet: []uint32{flag_account_success}, - FlagReset: []uint32{flag_api_error, flag_account_pending}, - }, - }, - { - name: "Test when the account is not yet on the sarafu network", + name: "Test when the account is not yet on the sarafu network", input: []byte("TrackingId1234"), response: &models.TrackStatusResult{ Active: false, }, - serverResponse: &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": false, - }, - }, +// serverResponse: &api.OKResponse{ +// Ok: true, +// Description: "Account creation succeeded", +// Result: map[string]any{ +// "active": false, +// }, +// }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_pending}, FlagReset: []uint32{flag_api_error, flag_account_success}, @@ -1104,7 +1086,6 @@ func TestCheckAccountStatus(t *testing.T) { // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.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, common.DATA_ACCOUNT_STATUS, status).Return(nil).Maybe() diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index 3b36ba7..bec48fd 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -4,7 +4,6 @@ import ( "context" "git.grassecon.net/urdt/ussd/internal/models" - "github.com/grassrootseconomics/eth-custodial/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/stretchr/testify/mock" ) @@ -24,13 +23,8 @@ func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) return args.Get(0).(*models.BalanceResult), args.Error(1) } -func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*api.OKResponse, error) { +func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { args := m.Called(trackingId) - return args.Get(0).(*api.OKResponse), args.Error(1) -} - -func (m *MockAccountService) CheckAccountStatus(ctx context.Context,publicKey string) (*models.TrackStatusResult, error) { - args := m.Called(publicKey) return args.Get(0).(*models.TrackStatusResult), args.Error(1) } diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 78a2798..a9a3085 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -5,7 +5,6 @@ import ( "encoding/json" "git.grassecon.net/urdt/ussd/internal/models" - "github.com/grassrootseconomics/eth-custodial/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -27,22 +26,12 @@ func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey strin return balanceResponse, nil } -func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { +func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { return &models.TrackStatusResult { Active: true, }, 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) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { return []dataserviceapi.TokenHoldings { dataserviceapi.TokenHoldings { diff --git a/remote/accountservice.go b/remote/accountservice.go index bc98cf1..24a469e 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -20,9 +20,7 @@ var ( type AccountServiceInterface interface { CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) CreateAccount(ctx context.Context) (*models.AccountResult, error) - // TODO: poorly named method seems to be checking transaction - CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) - TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) + TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) } @@ -38,7 +36,7 @@ type AccountService 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(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { +func (as *AccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { var r models.TrackStatusResult ep, err := url.JoinPath(config.TrackURL, trackingId) @@ -59,60 +57,13 @@ func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId str return &r, nil } -func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { - var okResponse api.OKResponse - var errResponse api.ErrResponse - var err error - - ep, err := url.JoinPath(config.TrackURL, publicKey) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("GET", ep, nil) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-GE-KEY", "xd") - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - errResponse.Description = err.Error() - return nil, err - } - 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") - } - return &okResponse, 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(ctx context.Context, publicKey string) (*models.BalanceResult, error) { var balanceResult models.BalanceResult - ep, err := url.JoinPath(config.BalanceURL, publicKey) + ep, err := url.JoinPath(config.BalanceURL, publicKey) if err != nil { return nil, err } From 3ae75b27a5cfa10726f2dcf3b2a7afe5a7807cfe Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 12:38:31 +0000 Subject: [PATCH 227/261] WIP replaced check account method but traversal crashes --- internal/handlers/ussd/menuhandler_test.go | 7 ++++--- remote/accountservice.go | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 7940704..6105657 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -20,7 +20,7 @@ import ( "git.grassecon.net/urdt/ussd/common" "github.com/alecthomas/assert/v2" - "github.com/grassrootseconomics/eth-custodial/pkg/api" +// "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" @@ -1048,7 +1048,7 @@ func TestCheckAccountStatus(t *testing.T) { tests := []struct { name string input []byte - serverResponse *api.OKResponse + //serverResponse *api.OKResponse response *models.TrackStatusResult expectedResult resource.Result }{ @@ -1086,7 +1086,8 @@ func TestCheckAccountStatus(t *testing.T) { // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) - mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) + //mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) + mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.response, nil) mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, status).Return(nil).Maybe() // Call the method under test diff --git a/remote/accountservice.go b/remote/accountservice.go index 24a469e..bc6a448 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -36,10 +36,10 @@ type AccountService 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) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { +func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { var r models.TrackStatusResult - ep, err := url.JoinPath(config.TrackURL, trackingId) + ep, err := url.JoinPath(config.TrackURL, publicKey) if err != nil { return nil, err } From 4bf56c525f5b0e9ac90e2c22fc68805327d03108 Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 13:19:44 +0000 Subject: [PATCH 228/261] Export voucher related code --- {internal/utils => common}/vouchers.go | 45 ++++++++++----------- {internal/utils => common}/vouchers_test.go | 2 +- internal/handlers/ussd/menuhandler.go | 10 ++--- 3 files changed, 28 insertions(+), 29 deletions(-) rename {internal/utils => common}/vouchers.go (74%) rename {internal/utils => common}/vouchers_test.go (99%) diff --git a/internal/utils/vouchers.go b/common/vouchers.go similarity index 74% rename from internal/utils/vouchers.go rename to common/vouchers.go index 0372700..291f28d 100644 --- a/internal/utils/vouchers.go +++ b/common/vouchers.go @@ -1,4 +1,4 @@ -package utils +package common import ( "context" @@ -6,7 +6,6 @@ import ( "strings" "git.grassecon.net/urdt/ussd/internal/storage" - "git.grassecon.net/urdt/ussd/common" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -96,13 +95,13 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, return } -// StoreTemporaryVoucher saves voucher metadata as temporary entries in the common.DataStore. -func StoreTemporaryVoucher(ctx context.Context, store common.DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { - entries := map[common.DataTyp][]byte{ - common.DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), - common.DATA_TEMPORARY_BAL: []byte(data.Balance), - common.DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), - common.DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), +// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore. +func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { + entries := map[DataTyp][]byte{ + DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), + DATA_TEMPORARY_BAL: []byte(data.Balance), + DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), + DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), } for key, value := range entries { @@ -113,13 +112,13 @@ func StoreTemporaryVoucher(ctx context.Context, store common.DataStore, sessionI return nil } -// GetTemporaryVoucherData retrieves temporary voucher metadata from the common.DataStore. -func GetTemporaryVoucherData(ctx context.Context, store common.DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) { - keys := []common.DataTyp{ - common.DATA_TEMPORARY_SYM, - common.DATA_TEMPORARY_BAL, - common.DATA_TEMPORARY_DECIMAL, - common.DATA_TEMPORARY_ADDRESS, +// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore. +func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) { + keys := []DataTyp{ + DATA_TEMPORARY_SYM, + DATA_TEMPORARY_BAL, + DATA_TEMPORARY_DECIMAL, + DATA_TEMPORARY_ADDRESS, } data := &dataserviceapi.TokenHoldings{} @@ -141,14 +140,14 @@ func GetTemporaryVoucherData(ctx context.Context, store common.DataStore, sessio return data, nil } -// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the common.DataStore. -func UpdateVoucherData(ctx context.Context, store common.DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { +// 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 *dataserviceapi.TokenHoldings) error { // Active voucher data entries - activeEntries := map[common.DataTyp][]byte{ - common.DATA_ACTIVE_SYM: []byte(data.TokenSymbol), - common.DATA_ACTIVE_BAL: []byte(data.Balance), - common.DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals), - common.DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress), + activeEntries := map[DataTyp][]byte{ + DATA_ACTIVE_SYM: []byte(data.TokenSymbol), + DATA_ACTIVE_BAL: []byte(data.Balance), + DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals), + DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress), } // Write active data diff --git a/internal/utils/vouchers_test.go b/common/vouchers_test.go similarity index 99% rename from internal/utils/vouchers_test.go rename to common/vouchers_test.go index c59037e..401d4d7 100644 --- a/internal/utils/vouchers_test.go +++ b/common/vouchers_test.go @@ -1,4 +1,4 @@ -package utils +package common import ( "context" diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ed5f466..2e075d7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1117,7 +1117,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - data := utils.ProcessVouchers(vouchersResp) + data := common.ProcessVouchers(vouchersResp) // Store all voucher data dataMap := map[string]string{ @@ -1167,7 +1167,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr) + metadata, err := common.GetVoucherData(ctx, h.prefixDb, inputStr) if err != nil { return res, fmt.Errorf("failed to retrieve voucher data: %v", err) } @@ -1177,7 +1177,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { + if err := common.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { return res, err } @@ -1198,13 +1198,13 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re // Get temporary data - tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) + tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { return res, err } // Set as active and clear temporary data - if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { + if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { return res, err } From 0b4bf58107cdbcc1c826eb3d8f0e63073335c0f1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 16:31:29 +0300 Subject: [PATCH 229/261] resolved failing tests due to tempData --- internal/handlers/ussd/menuhandler_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index a960cfc..6672ab1 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2119,14 +2119,6 @@ func TestSetVoucher(t *testing.T) { utils.DATA_ACTIVE_ADDRESS: []byte(tempData.ContractAddress), } - // 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.TokenSymbol), nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) @@ -2138,11 +2130,6 @@ func TestSetVoucher(t *testing.T) { 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 ce917d9e89b1591aff9fe1a181c29286957355fd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 17:05:06 +0300 Subject: [PATCH 230/261] 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)) } }) } From ac0b4b2ed1de7ab609441b67feaba1c5a58fcafe Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:08:30 +0300 Subject: [PATCH 231/261] pass context --- internal/testutil/TestEngine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index bd6bc7f..26b889e 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -73,7 +73,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userDataStore) lhs.SetPersister(pe) From 12825ae08a268db47ce105271c41807d1130913a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:10:46 +0300 Subject: [PATCH 232/261] setup adminstore in the local handler service --- internal/handlers/handlerservice.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 247e6c5..5990dc8 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -1,6 +1,8 @@ package handlers import ( + "context" + "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/engine" @@ -34,16 +36,21 @@ type LocalHandlerService struct { Rs resource.Resource } -func NewLocalHandlerService(fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) { +func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) { parser, err := getParser(fp, debug) if err != nil { return nil, err } + adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + if err != nil { + return nil, err + } return &LocalHandlerService{ - Parser: parser, - DbRs: dbResource, - Cfg: cfg, - Rs: rs, + Parser: parser, + DbRs: dbResource, + AdminStore: adminstore, + Cfg: cfg, + Rs: rs, }, nil } From 5abe9b78cc66181271cc4fdacfd305df558474b3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:11:26 +0300 Subject: [PATCH 233/261] attach an admin store for the phone numbers --- cmd/africastalking/main.go | 8 +++++++- cmd/async/main.go | 9 ++++++++- cmd/http/main.go | 9 ++++++++- cmd/main.go | 13 +++++++------ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 79fb091..ad56467 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -131,7 +131,7 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { @@ -139,6 +139,12 @@ func main() { os.Exit(1) } + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/async/main.go b/cmd/async/main.go index 902dfc7..bdb17a6 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -104,8 +104,15 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) + + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) diff --git a/cmd/http/main.go b/cmd/http/main.go index ece882e..39e311e 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -92,13 +92,20 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } + + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index 7dd3cd6..95b84c9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,7 +15,6 @@ 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/utils" ) var ( @@ -49,9 +48,6 @@ func main() { ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") - as, _ := utils.NewAdminStore(ctx, "admin_numbers") - as.Seed() - cfg := engine.Config{ Root: "root", SessionId: sessionId, @@ -92,9 +88,8 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdatastore) - lhs.SetAdminStore(as) lhs.SetPersister(pe) if err != nil { @@ -102,6 +97,12 @@ func main() { os.Exit(1) } + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { From b2655b7f117fa5e0c34d5be2e474d902d31df596 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:59:11 +0300 Subject: [PATCH 234/261] remove seed from executable --- cmd/africastalking/main.go | 8 +------- cmd/async/main.go | 6 ------ cmd/http/main.go | 8 +------- cmd/main.go | 6 ------ 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index ad56467..891301a 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -131,7 +131,7 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { @@ -139,12 +139,6 @@ func main() { os.Exit(1) } - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/async/main.go b/cmd/async/main.go index bdb17a6..df11dac 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -107,12 +107,6 @@ func main() { lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) diff --git a/cmd/http/main.go b/cmd/http/main.go index 39e311e..df37878 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -92,7 +92,7 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { @@ -100,12 +100,6 @@ func main() { os.Exit(1) } - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index 95b84c9..848068f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -97,12 +97,6 @@ func main() { os.Exit(1) } - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { From 299534ccf1701bf2cbddc7cb68ac34d846745e33 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:59:51 +0300 Subject: [PATCH 235/261] define seed as a command in the devtool --- devtools/commands/seed.go | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 devtools/commands/seed.go diff --git a/devtools/commands/seed.go b/devtools/commands/seed.go new file mode 100644 index 0000000..8abd50f --- /dev/null +++ b/devtools/commands/seed.go @@ -0,0 +1,47 @@ +package commands + +import ( + "context" + "encoding/json" + "os" + + "git.defalsify.org/vise.git/logging" + "git.grassecon.net/urdt/ussd/internal/utils" +) + +var ( + logg = logging.NewVanilla().WithDomain("adminstore") +) + +type Admin struct { + PhoneNumber string `json:"phonenumber"` +} + +type Config struct { + Admins []Admin `json:"admins"` +} + +func Seed(ctx context.Context) error { + var config Config + adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + store := adminstore.FsStore + if err != nil { + return err + } + defer store.Close() + data, err := os.ReadFile("admin_numbers.json") + if err != nil { + return err + } + if err := json.Unmarshal(data, &config); err != nil { + return err + } + for _, admin := range config.Admins { + err := store.Put(ctx, []byte(admin.PhoneNumber), []byte("1")) + if err != nil { + logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber) + return err + } + } + return nil +} From 7aab3cff8ca8dda7feafb2fba15d71bf601f8894 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:00:16 +0300 Subject: [PATCH 236/261] remove seed --- internal/utils/adminstore.go | 40 +++--------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go index 43a3bfd..a492479 100644 --- a/internal/utils/adminstore.go +++ b/internal/utils/adminstore.go @@ -2,8 +2,6 @@ package utils import ( "context" - "encoding/json" - "os" "git.defalsify.org/vise.git/db" fsdb "git.defalsify.org/vise.git/db/fs" @@ -14,17 +12,9 @@ var ( logg = logging.NewVanilla().WithDomain("adminstore") ) -type Admin struct { - PhoneNumber string `json:"phonenumber"` -} - -type Config struct { - Admins []Admin `json:"admins"` -} - type AdminStore struct { ctx context.Context - fsStore db.Db + FsStore db.Db } func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) { @@ -32,7 +22,7 @@ func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) { if err != nil { return nil, err } - return &AdminStore{ctx: ctx, fsStore: fsStore}, nil + return &AdminStore{ctx: ctx, FsStore: fsStore}, nil } func getFsStore(ctx context.Context, connectStr string) (db.Db, error) { @@ -45,33 +35,9 @@ func getFsStore(ctx context.Context, connectStr string) (db.Db, error) { return fsStore, nil } -// Seed initializes a list of phonenumbers with admin privileges -func (as *AdminStore) Seed() error { - var config Config - - store := as.fsStore - defer store.Close() - - data, err := os.ReadFile("admin_numbers.json") - if err != nil { - return err - } - if err := json.Unmarshal(data, &config); err != nil { - return err - } - for _, admin := range config.Admins { - err := store.Put(as.ctx, []byte(admin.PhoneNumber), []byte("1")) - if err != nil { - logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber) - return err - } - } - return nil -} - // Checks if the given sessionId is listed as an admin. func (as *AdminStore) IsAdmin(sessionId string) (bool, error) { - _, err := as.fsStore.Get(as.ctx, []byte(sessionId)) + _, err := as.FsStore.Get(as.ctx, []byte(sessionId)) if err != nil { if db.IsNotFound(err) { logg.Printf(logging.LVL_INFO, "Returning false because session id was not found") From 7fa38340ddf3cfaab0207506d048db32bf82dc52 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:00:41 +0300 Subject: [PATCH 237/261] add command to initialize a list of admin numbers --- devtools/main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 devtools/main.go diff --git a/devtools/main.go b/devtools/main.go new file mode 100644 index 0000000..d0d4632 --- /dev/null +++ b/devtools/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "context" + "log" + + "git.grassecon.net/urdt/ussd/devtools/commands" +) + +func main() { + ctx := context.Background() + err := commands.Seed(ctx) + if err != nil { + log.Fatalf("Failed to initialize a list of admins with error %s", err) + } + +} From 53fa6f64ce209bac95129a1815f1edcd01ff369e Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:01:01 +0300 Subject: [PATCH 238/261] define structure of json --- devtools/admin_numbers.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 devtools/admin_numbers.json diff --git a/devtools/admin_numbers.json b/devtools/admin_numbers.json new file mode 100644 index 0000000..ca58a23 --- /dev/null +++ b/devtools/admin_numbers.json @@ -0,0 +1,7 @@ +{ + "admins": [ + { + "phonenumber" : "" + } + ] +} \ No newline at end of file From 7d1a04f089547c90b04d328a99298cb2bc24de99 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:01:24 +0300 Subject: [PATCH 239/261] remove from root --- admin_numbers.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 admin_numbers.json diff --git a/admin_numbers.json b/admin_numbers.json deleted file mode 100644 index ca58a23..0000000 --- a/admin_numbers.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "admins": [ - { - "phonenumber" : "" - } - ] -} \ No newline at end of file From f4ca4454ea2f61aa798f13253b011dcd414a94ab Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 00:42:23 +0300 Subject: [PATCH 240/261] use a single DATA_TEMPORARY_VALUE for the PIN and voucher data --- internal/handlers/ussd/menuhandler.go | 8 ++-- internal/handlers/ussd/menuhandler_test.go | 26 +++++-------- internal/utils/db.go | 6 +-- internal/utils/vouchers.go | 43 +++++++--------------- internal/utils/vouchers_test.go | 17 +++------ 5 files changed, 34 insertions(+), 66 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dd3efdb..aaa1530 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -222,7 +222,7 @@ 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 +// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE // 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) { @@ -247,7 +247,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_incorrect_pin) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { return res, err } @@ -264,7 +264,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -295,7 +295,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6672ab1..bf29926 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" @@ -271,7 +271,7 @@ func TestSaveTemporaryPin(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_TEMPORARY_PIN, []byte(tt.input)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -1013,7 +1013,7 @@ func TestVerifyCreatePin(t *testing.T) { }, } - typ := utils.DATA_TEMPORARY_PIN + typ := utils.DATA_TEMPORARY_VALUE for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1806,7 +1806,7 @@ func TestConfirmPin(t *testing.T) { // Set up the expected behavior of the mock mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) @@ -2078,16 +2078,9 @@ func TestViewVoucher(t *testing.T) { } // 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", - } + expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce") - for dataType, dataValue := range expectedData { - mockDataStore.On("WriteEntry", ctx, sessionId, dataType, []byte(dataValue)).Return(nil) - } + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) assert.NoError(t, err) @@ -2111,6 +2104,8 @@ func TestSetVoucher(t *testing.T) { ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } + expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9") + // Define the expected active entries activeEntries := map[utils.DataTyp][]byte{ utils.DATA_ACTIVE_SYM: []byte(tempData.TokenSymbol), @@ -2120,10 +2115,7 @@ func TestSetVoucher(t *testing.T) { } // Mocking ReadEntry calls for temporary data retrieval - 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.TokenDecimals), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.ContractAddress), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) // Mocking WriteEntry calls for setting active data for key, value := range activeEntries { diff --git a/internal/utils/db.go b/internal/utils/db.go index 802e092..df96dfa 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -22,16 +22,12 @@ const ( DATA_OFFERINGS DATA_RECIPIENT DATA_AMOUNT - DATA_TEMPORARY_PIN + DATA_TEMPORARY_VALUE DATA_VOUCHER_LIST - DATA_TEMPORARY_SYM DATA_ACTIVE_SYM - DATA_TEMPORARY_BAL DATA_ACTIVE_BAL DATA_PUBLIC_KEY_REVERSE - DATA_TEMPORARY_DECIMAL DATA_ACTIVE_DECIMAL - DATA_TEMPORARY_ADDRESS DATA_ACTIVE_ADDRESS ) diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go index b027cc1..1c20492 100644 --- a/internal/utils/vouchers.go +++ b/internal/utils/vouchers.go @@ -77,7 +77,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, for i, sym := range symList { parts := strings.SplitN(sym, ":", 2) - + if input == parts[0] || strings.EqualFold(input, parts[1]) { symbol = parts[1] if i < len(balList) { @@ -97,45 +97,30 @@ 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 *dataserviceapi.TokenHoldings) error { - entries := map[DataTyp][]byte{ - DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), - DATA_TEMPORARY_BAL: []byte(data.Balance), - DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), - DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), + tempData := fmt.Sprintf("%s,%s,%s,%s", data.TokenSymbol, data.Balance, data.TokenDecimals, data.ContractAddress) + + if err := store.WriteEntry(ctx, sessionId, DATA_TEMPORARY_VALUE, []byte(tempData)); err != nil { + return err } - 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) (*dataserviceapi.TokenHoldings, error) { - keys := []DataTyp{ - DATA_TEMPORARY_SYM, - DATA_TEMPORARY_BAL, - DATA_TEMPORARY_DECIMAL, - DATA_TEMPORARY_ADDRESS, + temp_data, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE) + if err != nil { + return nil, err } + values := strings.SplitN(string(temp_data), ",", 4) + data := &dataserviceapi.TokenHoldings{} - 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.TokenSymbol = string(values[0]) - data.Balance = string(values[1]) - data.TokenDecimals = string(values[2]) - data.ContractAddress = string(values[3]) + data.TokenSymbol = values[0] + data.Balance = values[1] + data.TokenDecimals = values[2] + data.ContractAddress = values[3] return data, nil } diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go index a609d27..c2ff4e3 100644 --- a/internal/utils/vouchers_test.go +++ b/internal/utils/vouchers_test.go @@ -2,6 +2,7 @@ package utils import ( "context" + "fmt" "testing" "git.grassecon.net/urdt/ussd/internal/storage" @@ -126,18 +127,12 @@ func TestStoreTemporaryVoucher(t *testing.T) { require.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"), - } + expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9") + + storedValue, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE) + require.NoError(t, err) + require.Equal(t, expectedData, string(storedValue), "Mismatch for key %v", DATA_TEMPORARY_VALUE) - for key, expectedValue := range expectedEntries { - storedValue, err := store.ReadEntry(ctx, sessionId, key) - require.NoError(t, err) - require.Equal(t, expectedValue, storedValue, "Mismatch for key %v", key) - } } func TestGetTemporaryVoucherData(t *testing.T) { From 63eed81d3d5ccb0c9433eda7b9fd289463c6d4f0 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 00:44:15 +0300 Subject: [PATCH 241/261] Merge branch 'master' into consolidate-temp-data-storage --- internal/handlers/ussd/menuhandler.go | 118 +++++++++++++------- internal/handlers/ussd/menuhandler_test.go | 52 +++++++-- internal/utils/db.go | 7 ++ 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 +- services/registration/set_female.vis | 2 +- services/registration/set_male.vis | 2 +- services/registration/set_unspecified.vis | 2 +- 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 + 18 files changed, 150 insertions(+), 59 deletions(-) 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/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index aaa1530..60dd11c 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 @@ -334,13 +333,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 } @@ -357,20 +361,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 } @@ -382,10 +390,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 } @@ -402,13 +419,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 } @@ -426,14 +450,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 @@ -447,12 +479,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 } } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index bf29926..f6f1069 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -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)) } }) } diff --git a/internal/utils/db.go b/internal/utils/db.go index df96dfa..4d29c48 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_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS + ) func typToBytes(typ DataTyp) []byte { 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 * 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 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 d7ea8fa65196e1de6fd1c08f4f0a408de1166ba8 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 01:10:58 +0300 Subject: [PATCH 242/261] Use the DATA_TEMPORARY_VALUE for the user data --- internal/handlers/ussd/menuhandler.go | 24 +++++++++++----------- internal/handlers/ussd/menuhandler_test.go | 18 ++++++++-------- internal/utils/db.go | 6 ------ 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 60dd11c..5f0c2d9 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -338,13 +338,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) 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) + temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { return res, err } @@ -368,13 +368,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME) + temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)) if err != nil { return res, err } @@ -396,13 +396,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB) + temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) 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)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { return res, err } @@ -426,13 +426,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION) + temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(location)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { return res, err } @@ -456,13 +456,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER) + temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) 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)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { return res, err } @@ -486,13 +486,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS) + temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index f6f1069..77ae68c 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -186,7 +186,7 @@ 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_TEMPORARY_FIRST_NAME, []byte(firstName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -219,7 +219,7 @@ 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_TEMPORARY_FAMILY_NAME, []byte(familyName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -310,7 +310,7 @@ 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_TEMPORARY_YOB, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -343,7 +343,7 @@ 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_TEMPORARY_LOCATION, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -376,7 +376,7 @@ 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_TEMPORARY_OFFERINGS, []byte(offerings)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -442,10 +442,10 @@ 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_TEMPORARY_GENDER + expectedKey := utils.DATA_TEMPORARY_VALUE mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store @@ -463,9 +463,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } }) } diff --git a/internal/utils/db.go b/internal/utils/db.go index 4d29c48..b37240d 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -14,17 +14,11 @@ 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 From 9a528cfd1440f72d0678d318e847e7d10fbdee69 Mon Sep 17 00:00:00 2001 From: lash Date: Fri, 1 Nov 2024 03:17:01 +0000 Subject: [PATCH 243/261] Clean up messily failed conflict resolution --- common/vouchers_test.go | 17 +++-- internal/handlers/ussd/menuhandler.go | 44 ++++++------- internal/handlers/ussd/menuhandler_test.go | 74 ++++------------------ 3 files changed, 44 insertions(+), 91 deletions(-) diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 95fce6e..8b9fa2a 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -9,13 +9,12 @@ import ( "github.com/stretchr/testify/require" "git.grassecon.net/urdt/ussd/internal/storage" - "git.grassecon.net/urdt/ussd/common" 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. -func InitializeTestDb(t *testing.T) (context.Context, *common.UserDataStore) { +func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) { ctx := context.Background() // Initialize memDb @@ -23,8 +22,8 @@ func InitializeTestDb(t *testing.T) (context.Context, *common.UserDataStore) { err := db.Connect(ctx, "") require.NoError(t, err, "Failed to connect to memDb") - // Create common.UserDataStore with memDb - store := &common.UserDataStore{Db: db} + // Create UserDataStore with memDb + store := &UserDataStore{Db: db} t.Cleanup(func() { db.Close() // Ensure the DB is closed after each test @@ -183,11 +182,11 @@ func TestUpdateVoucherData(t *testing.T) { require.NoError(t, err) // Verify active data was stored correctly - activeEntries := map[common.DataTyp][]byte{ - common.DATA_ACTIVE_SYM: []byte(newData.TokenSymbol), - common.DATA_ACTIVE_BAL: []byte(newData.Balance), - common.DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals), - common.DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress), + activeEntries := map[DataTyp][]byte{ + DATA_ACTIVE_SYM: []byte(newData.TokenSymbol), + DATA_ACTIVE_BAL: []byte(newData.Balance), + DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals), + DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress), } for key, expectedValue := range activeEntries { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index db8d520..42785d7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -20,7 +20,7 @@ import ( "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/remote" "git.grassecon.net/urdt/ussd/internal/utils" "gopkg.in/leonelquinteros/gotext.v1" @@ -246,7 +246,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_incorrect_pin) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { return res, err } @@ -263,7 +263,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -294,7 +294,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -338,13 +338,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) 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_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) + temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { return res, err } @@ -369,13 +369,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) + temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) if err != nil { return res, err } @@ -397,13 +397,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob)) + temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { return res, err } @@ -427,13 +427,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) + temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { return res, err } @@ -457,13 +457,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) + temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { return res, err } @@ -488,13 +488,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) + temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 30f84d9..2eb6519 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -19,7 +19,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "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" @@ -181,11 +180,7 @@ func TestSaveFirstname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_FIRST_NAME, []byte(firstName)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -218,11 +213,7 @@ func TestSaveFamilyname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -284,11 +275,7 @@ func TestSaveTemporaryPin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -317,11 +304,7 @@ func TestSaveYoB(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_YOB, []byte(yob)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -354,11 +337,7 @@ func TestSaveLocation(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_LOCATION, []byte(yob)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -391,11 +370,7 @@ func TestSaveOfferings(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_OFFERINGS, []byte(offerings)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -461,17 +436,10 @@ func TestSaveGender(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database if tt.expectCall { -<<<<<<< HEAD - expectedKey := common.DATA_GENDER + expectedKey := common.DATA_TEMPORARY_VALUE mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) -======= - expectedKey := utils.DATA_TEMPORARY_VALUE - mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) - } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store @@ -489,15 +457,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { -<<<<<<< HEAD - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) -======= - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) - } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) ->>>>>>> master + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } }) } @@ -1072,11 +1034,7 @@ func TestVerifyCreatePin(t *testing.T) { }, } -<<<<<<< HEAD - typ := common.DATA_TEMPORARY_PIN -======= - typ := utils.DATA_TEMPORARY_VALUE ->>>>>>> master + typ := common.DATA_TEMPORARY_VALUE for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1816,11 +1774,7 @@ func TestConfirmPin(t *testing.T) { // Set up the expected behavior of the mock mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) -<<<<<<< HEAD - mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) -======= - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) ->>>>>>> master + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) @@ -2060,7 +2014,7 @@ func TestViewVoucher(t *testing.T) { // Set up expectations for mockDataStore expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce") - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) assert.NoError(t, err) @@ -2095,7 +2049,7 @@ func TestSetVoucher(t *testing.T) { } // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) // Mocking WriteEntry calls for setting active data for key, value := range activeEntries { From 0014693ba8b3b8f1545e59e3cf66bf6bf71caebb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 06:39:37 +0300 Subject: [PATCH 244/261] remove guard pin option --- menutraversal_test/group_test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 8b765f5..a219a6c 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -13,7 +13,7 @@ }, { "input": "5", - "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n3:Guard my PIN\n0:Back" + "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n0:Back" }, { "input": "1", From 7e448f739ab0205b8e3bab16b0081344de573868 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 09:35:48 +0300 Subject: [PATCH 245/261] change fs store path to root --- devtools/commands/seed.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/commands/seed.go b/devtools/commands/seed.go index 8abd50f..e76c83d 100644 --- a/devtools/commands/seed.go +++ b/devtools/commands/seed.go @@ -23,7 +23,7 @@ type Config struct { func Seed(ctx context.Context) error { var config Config - adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + adminstore, err := utils.NewAdminStore(ctx, "../admin_numbers") store := adminstore.FsStore if err != nil { return err From eb2c73dce1478023a34e2d552a649a9c14339d41 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 11:51:50 +0300 Subject: [PATCH 246/261] remove unused setadmin store --- internal/handlers/handlerservice.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 5990dc8..cbc2a29 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -62,10 +62,6 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } -func (ls *LocalHandlerService) SetAdminStore(adminstore *utils.AdminStore) { - ls.AdminStore = adminstore -} - func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) if err != nil { From cfe3e526dfb1793ab4977998ae730c834ac67d5e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 13:17:45 +0300 Subject: [PATCH 247/261] Remove the subprefixdbmock --- internal/handlers/ussd/menuhandler_test.go | 87 +++++++++++++++------- internal/testutil/mocks/subprefixdbmock.go | 21 ------ 2 files changed, 59 insertions(+), 49 deletions(-) delete mode 100644 internal/testutil/mocks/subprefixdbmock.go diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 77ae68c..6f70108 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -16,6 +16,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" @@ -26,6 +27,7 @@ import ( testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" + memdb "git.defalsify.org/vise.git/db/mem" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -2021,19 +2023,24 @@ func TestSetDefaultVoucher(t *testing.T) { func TestCheckVouchers(t *testing.T) { mockDataStore := new(mocks.MockUserDataStore) mockAccountService := new(mocks.MockAccountService) - mockSubPrefixDb := new(mocks.MockSubPrefixDb) sessionId := "session123" publicKey := "0X13242618721" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) h := &Handlers{ userdataStore: mockDataStore, accountService: mockAccountService, - prefixDb: mockSubPrefixDb, + prefixDb: spdb, } - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) mockVouchersResponse := &models.VoucherHoldingResponse{} @@ -2042,38 +2049,53 @@ func TestCheckVouchers(t *testing.T) { {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } + expectedSym := []byte("1:SRF\n2:MILO") + 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("")) - + _, err = h.CheckVouchers(ctx, "check_vouchers", []byte("")) assert.NoError(t, err) + // Read voucher sym data from the store + voucherData, err := spdb.Get(ctx, []byte("sym")) + if err != nil { + t.Fatal(err) + } + + // assert that the data is stored correctly + assert.Equal(t, expectedSym, voucherData) + mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) } func TestGetVoucherList(t *testing.T) { - mockSubPrefixDb := new(mocks.MockSubPrefixDb) - sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + h := &Handlers{ - prefixDb: mockSubPrefixDb, + prefixDb: spdb, } - mockSubPrefixDb.On("Get", ctx, []byte("sym")).Return([]byte("1:SRF\n2:MILO"), nil) + expectedSym := []byte("1:SRF\n2:MILO") + + // Put voucher sym data from the store + err = spdb.Put(ctx, []byte("sym"), expectedSym) + if err != nil { + t.Fatal(err) + } res, err := h.GetVoucherList(ctx, "", []byte("")) - assert.NoError(t, err) - assert.Contains(t, res.Content, "1:SRF\n2:MILO") - mockSubPrefixDb.AssertExpectations(t) + assert.NoError(t, err) + assert.Equal(t, res.Content, string(expectedSym)) } func TestViewVoucher(t *testing.T) { @@ -2082,27 +2104,37 @@ func TestViewVoucher(t *testing.T) { t.Logf(err.Error()) } mockDataStore := new(mocks.MockUserDataStore) - mockSubPrefixDb := new(mocks.MockSubPrefixDb) sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) + db := memdb.NewMemDb() + err = db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + h := &Handlers{ userdataStore: mockDataStore, flagManager: fm.parser, - prefixDb: mockSubPrefixDb, + prefixDb: spdb, } // Define mock voucher data - mockVoucherData := map[string]string{ - "sym": "1:SRF", - "bal": "1:100", - "deci": "1:6", - "addr": "1:0xd4c288865Ce", + 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"), } - for key, value := range mockVoucherData { - mockSubPrefixDb.On("Get", ctx, []byte(key)).Return([]byte(value), nil) + // Put the data + for key, value := range mockData { + err = spdb.Put(ctx, []byte(key), []byte(value)) + if err != nil { + t.Fatal(err) + } } // Set up expectations for mockDataStore @@ -2115,7 +2147,6 @@ func TestViewVoucher(t *testing.T) { assert.Contains(t, res.Content, "SRF\n100") mockDataStore.AssertExpectations(t) - mockSubPrefixDb.AssertExpectations(t) } func TestSetVoucher(t *testing.T) { diff --git a/internal/testutil/mocks/subprefixdbmock.go b/internal/testutil/mocks/subprefixdbmock.go deleted file mode 100644 index e98bcb6..0000000 --- a/internal/testutil/mocks/subprefixdbmock.go +++ /dev/null @@ -1,21 +0,0 @@ -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 332074375aee0010d9cbf37f8738bf12e432810d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 16:44:54 +0300 Subject: [PATCH 248/261] wrap in devtools/admin --- devtools/{ => admin}/admin_numbers.json | 0 devtools/{ => admin}/commands/seed.go | 0 devtools/{ => admin}/main.go | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename devtools/{ => admin}/admin_numbers.json (100%) rename devtools/{ => admin}/commands/seed.go (100%) rename devtools/{ => admin}/main.go (79%) diff --git a/devtools/admin_numbers.json b/devtools/admin/admin_numbers.json similarity index 100% rename from devtools/admin_numbers.json rename to devtools/admin/admin_numbers.json diff --git a/devtools/commands/seed.go b/devtools/admin/commands/seed.go similarity index 100% rename from devtools/commands/seed.go rename to devtools/admin/commands/seed.go diff --git a/devtools/main.go b/devtools/admin/main.go similarity index 79% rename from devtools/main.go rename to devtools/admin/main.go index d0d4632..9a527f3 100644 --- a/devtools/main.go +++ b/devtools/admin/main.go @@ -4,7 +4,7 @@ import ( "context" "log" - "git.grassecon.net/urdt/ussd/devtools/commands" + "git.grassecon.net/urdt/ussd/devtools/admin/commands" ) func main() { From 6dbe74d12bc777285b68837fc9c4d0b470559607 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 16:46:09 +0300 Subject: [PATCH 249/261] use single temporary value --- 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 198d45b..feb002e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -237,7 +237,7 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -312,7 +312,7 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input if err != nil { return res, err } - err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { return res, err } @@ -845,7 +845,7 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } From c52f8312e4641cb684cf9c206b91b89b5617a2ff Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 2 Nov 2024 03:21:38 +0300 Subject: [PATCH 250/261] Remove the dbmock and userdbmock --- internal/testutil/mocks/dbmock.go | 59 --------------------------- internal/testutil/mocks/userdbmock.go | 24 ----------- 2 files changed, 83 deletions(-) delete mode 100644 internal/testutil/mocks/dbmock.go delete mode 100644 internal/testutil/mocks/userdbmock.go diff --git a/internal/testutil/mocks/dbmock.go b/internal/testutil/mocks/dbmock.go deleted file mode 100644 index 0b40eab..0000000 --- a/internal/testutil/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/testutil/mocks/userdbmock.go b/internal/testutil/mocks/userdbmock.go deleted file mode 100644 index ff3f18d..0000000 --- a/internal/testutil/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 476a69fe1b7fcd67728a9eaf5f5526b4010d4a1e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 2 Nov 2024 03:22:33 +0300 Subject: [PATCH 251/261] Update menuhandler tests to use the memdb instead of dbmocks --- internal/handlers/ussd/menuhandler_test.go | 1185 +++++++++----------- 1 file changed, 510 insertions(+), 675 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6f70108..48eb81a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -7,10 +7,7 @@ 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" @@ -20,7 +17,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" - "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" @@ -36,15 +32,46 @@ var ( flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") ) +// InitializeTestStore sets up and returns an in-memory database and store. +func InitializeTestStore(t *testing.T) (context.Context, *utils.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 := &utils.UserDataStore{Db: db} + + t.Cleanup(func() { + db.Close() // Ensure the DB is closed after each test + }) + + return ctx, store +} + +func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storage.SubPrefixDb { + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + + return spdb +} + func TestNewHandlers(t *testing.T) { + _, store := InitializeTestStore(t) + fm, err := NewFlagManager(flagsPath) accountService := testservice.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, &accountService) + handlers, err := NewHandlers(fm.parser, store, &accountService) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -58,10 +85,7 @@ func TestNewHandlers(t *testing.T) { // Test case for nil userdataStore t.Run("Nil UserDataStore", func(t *testing.T) { - appFlags := &asm.FlagParser{} - - handlers, err := NewHandlers(appFlags, nil, &accountService) - + handlers, err := NewHandlers(fm.parser, nil, &accountService) if err == nil { t.Fatal("expected an error, got none") } @@ -75,19 +99,19 @@ func TestNewHandlers(t *testing.T) { } func TestCreateAccount(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - // Create required mocks + flag_account_created, err := fm.GetFlag("flag_account_created") if err != nil { t.Logf(err.Error()) } - // Define session ID and mock data - sessionId := "session123" - notFoundErr := db.ErrNotFound{} - ctx := context.WithValue(context.Background(), "SessionId", sessionId) tests := []struct { name string @@ -111,47 +135,24 @@ func TestCreateAccount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAccountService := new(mocks.MockAccountService) - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - - // Create a Handlers instance with the mock data store h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, 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: publicKey, - } - - 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) - } - 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) + mockAccountService.On("CreateAccount").Return(tt.serverResponse, nil) // Call the method you want to test - res, err := h.CreateAccount(ctx, "create_account", []byte("some-input")) + res, err := h.CreateAccount(ctx, "create_account", []byte("")) // 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) }) } } @@ -176,23 +177,28 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) { } func TestSaveFirstname(t *testing.T) { - // Create new mocks - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) - fm, err := NewFlagManager(flagsPath) + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) // Define test data - sessionId := "session123" firstName := "John" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, flagManager: fm.parser, st: mockState, } @@ -204,28 +210,34 @@ func TestSaveFirstname(t *testing.T) { assert.NoError(t, err) assert.Equal(t, resource.Result{}, res) - // Assert all expectations were met - mockStore.AssertExpectations(t) + // Verify that the DATA_FIRST_NAME entry has been updated with the temporary value + storedFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME) + assert.Equal(t, firstName, string(storedFirstName)) } func TestSaveFamilyname(t *testing.T) { - // Create a new instance of UserDataStore - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) - fm, err := NewFlagManager(flagsPath) + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) // Define test data - sessionId := "session123" familyName := "Doeee" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, st: mockState, flagManager: fm.parser, } @@ -237,25 +249,213 @@ func TestSaveFamilyname(t *testing.T) { assert.NoError(t, err) assert.Equal(t, resource.Result{}, res) - // Assert all expectations were met - mockStore.AssertExpectations(t) + // Verify that the DATA_FAMILY_NAME entry has been updated with the temporary value + storedFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME) + assert.Equal(t, familyName, string(storedFamilyName)) +} + +func TestSaveYoB(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test data + yob := "1980" + + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { + t.Fatal(err) + } + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + st: mockState, + } + + // Call the method + res, err := h.SaveYob(ctx, "save_yob", []byte(yob)) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_YOB entry has been updated with the temporary value + storedYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_YOB) + assert.Equal(t, yob, string(storedYob)) +} + +func TestSaveLocation(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test data + location := "Kilifi" + + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { + t.Fatal(err) + } + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + st: mockState, + } + + // Call the method + res, err := h.SaveLocation(ctx, "save_location", []byte(location)) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_LOCATION entry has been updated with the temporary value + storedLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION) + assert.Equal(t, location, string(storedLocation)) +} + +func TestSaveOfferings(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test data + offerings := "Bananas" + + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil { + t.Fatal(err) + } + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + st: mockState, + } + + // Call the method + res, err := h.SaveOfferings(ctx, "save_offerings", []byte(offerings)) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_OFFERINGS entry has been updated with the temporary value + storedOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS) + assert.Equal(t, offerings, string(storedOfferings)) +} + +func TestSaveGender(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test cases + tests := []struct { + name string + input []byte + expectedGender string + executingSymbol string + }{ + { + name: "Valid Male Input", + input: []byte("1"), + expectedGender: "male", + executingSymbol: "set_male", + }, + { + name: "Valid Female Input", + input: []byte("2"), + expectedGender: "female", + executingSymbol: "set_female", + }, + { + name: "Valid Unspecified Input", + input: []byte("3"), + executingSymbol: "set_unspecified", + expectedGender: "unspecified", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil { + t.Fatal(err) + } + + mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + st: mockState, + flagManager: fm.parser, + } + + // Call the method + res, err := h.SaveGender(ctx, "save_gender", tt.input) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_GENDER entry has been updated with the temporary value + storedGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_GENDER) + assert.Equal(t, tt.expectedGender, string(storedGender)) + }) + } } func TestSaveTemporaryPin(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) - mockStore := new(mocks.MockUserDataStore) if err != nil { log.Fatal(err) } + flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, - userdataStore: mockStore, + userdataStore: store, } - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Define test cases tests := []struct { @@ -281,10 +481,6 @@ func TestSaveTemporaryPin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) - // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -293,205 +489,27 @@ func TestSaveTemporaryPin(t *testing.T) { } // Assert that the Result FlagSet has the required flags after language switch - assert.Equal(t, res, tt.expectedResult, "Flags should be equal to account created") - - }) - } -} - -func TestSaveYoB(t *testing.T) { - // Create new instances - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) - - fm, err := NewFlagManager(flagsPath) - - // Define test data - sessionId := "session123" - yob := "1980" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveYob(ctx, "save_yob", []byte(yob)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(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" - yob := "Kilifi" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveLocation(ctx, "save_location", []byte(yob)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(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" - offerings := "Bananas" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveOfferings(ctx, "save_offerings", []byte(offerings)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(t) -} - -func TestSaveGender(t *testing.T) { - // 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) - - // Define test cases - tests := []struct { - name string - input []byte - expectedGender string - expectCall bool - executingSymbol string - }{ - { - name: "Valid Male Input", - input: []byte("1"), - expectedGender: "male", - executingSymbol: "set_male", - expectCall: true, - }, - { - name: "Valid Female Input", - input: []byte("2"), - expectedGender: "female", - executingSymbol: "set_female", - expectCall: true, - }, - { - name: "Valid Unspecified Input", - input: []byte("3"), - executingSymbol: "set_unspecified", - expectedGender: "unspecified", - expectCall: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set up expectations for the mock database - if tt.expectCall { - expectedKey := utils.DATA_TEMPORARY_VALUE - mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) - } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []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 - _, err := h.SaveGender(ctx, "save_gender", tt.input) - - // Assert no error - assert.NoError(t, err) - - // Verify expectations - if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) - } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) - } + assert.Equal(t, res, tt.expectedResult, "Result should match expected result") }) } } func TestCheckIdentifier(t *testing.T) { - // Create a new instance of MockMyDataStore - mockStore := new(mocks.MockUserDataStore) - - // Define the session ID and context sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) // Define test cases tests := []struct { name string - mockPublicKey []byte + publicKey []byte mockErr error expectedContent string expectError bool }{ { name: "Saved public Key", - mockPublicKey: []byte("0xa8363"), + publicKey: []byte("0xa8363"), mockErr: nil, expectedContent: "0xa8363", expectError: false, @@ -500,39 +518,33 @@ func TestCheckIdentifier(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Set up expectations for the mock database - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) + err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(tt.publicKey)) + if err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, } // Call the method res, err := h.CheckIdentifier(ctx, "check_identifier", nil) // Assert results - assert.NoError(t, err) assert.Equal(t, tt.expectedContent, res.Content) - - // Verify expectations - mockStore.AssertExpectations(t) }) } } func TestGetSender(t *testing.T) { - mockStore := new(mocks.MockUserDataStore) + sessionId := "session123" + ctx, _ := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) - // Define test data - sessionId := "254712345678" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - } + // Create the Handlers instance + h := &Handlers{} // Call the method res, _ := h.GetSender(ctx, "get_sender", []byte("")) @@ -542,21 +554,27 @@ func TestGetSender(t *testing.T) { } func TestGetAmount(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) // Define test data - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) amount := "0.03" activeSym := "SRF" - // Set up the expected behavior of the mock - 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) + err := store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(amount)) + if err != nil { + t.Fatal(err) + } + + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(activeSym)) + if err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, } // Call the method @@ -566,31 +584,30 @@ func TestGetAmount(t *testing.T) { //Assert that the retrieved amount is what was set as the content assert.Equal(t, formattedAmount, res.Content) - } func TestGetRecipient(t *testing.T) { - mockStore := new(mocks.MockUserDataStore) - - // Define test data sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + recepient := "0xcasgatweksalw1018221" - // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return([]byte(recepient), nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recepient)) + if err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, } // Call the method - res, _ := h.GetRecipient(ctx, "get_recipient", []byte("Getting recipient...")) + res, _ := h.GetRecipient(ctx, "get_recipient", []byte("")) //Assert that the retrieved recepient is what was set as the content assert.Equal(t, recepient, res.Content) - } func TestGetFlag(t *testing.T) { @@ -608,12 +625,11 @@ func TestGetFlag(t *testing.T) { } func TestSetLanguage(t *testing.T) { - // Create a new instance of the Flag Manager fm, err := NewFlagManager(flagsPath) - if err != nil { log.Fatal(err) } + // Define test cases tests := []struct { name string @@ -652,25 +668,24 @@ func TestSetLanguage(t *testing.T) { // Call the method res, err := h.SetLanguage(context.Background(), "set_language", nil) - if err != nil { t.Error(err) } // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should match expected result") - }) } } + func TestResetAllowUpdate(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_allow_update, _ := fm.parser.GetFlag("flag_allow_update") - if err != nil { log.Fatal(err) } + + flag_allow_update, _ := fm.parser.GetFlag("flag_allow_update") + // Define test cases tests := []struct { name string @@ -688,7 +703,6 @@ func TestResetAllowUpdate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, @@ -696,13 +710,12 @@ func TestResetAllowUpdate(t *testing.T) { // Call the method res, err := h.ResetAllowUpdate(context.Background(), "reset_allow update", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Flags should be equal to account created") - }) } } @@ -740,25 +753,24 @@ func TestResetAccountAuthorized(t *testing.T) { // Call the method res, err := h.ResetAccountAuthorized(context.Background(), "reset_account_authorized", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestIncorrectPinReset(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") - if err != nil { log.Fatal(err) } + + flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") + // Define test cases tests := []struct { name string @@ -776,7 +788,6 @@ func TestIncorrectPinReset(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, @@ -784,25 +795,24 @@ func TestIncorrectPinReset(t *testing.T) { // Call the method res, err := h.ResetIncorrectPin(context.Background(), "reset_incorrect_pin", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestResetIncorrectYob(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_incorrect_date_format, _ := fm.parser.GetFlag("flag_incorrect_date_format") - if err != nil { log.Fatal(err) } + + flag_incorrect_date_format, _ := fm.parser.GetFlag("flag_incorrect_date_format") + // Define test cases tests := []struct { name string @@ -820,7 +830,6 @@ func TestResetIncorrectYob(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, @@ -828,43 +837,39 @@ func TestResetIncorrectYob(t *testing.T) { // Call the method res, err := h.ResetIncorrectYob(context.Background(), "reset_incorrect_yob", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestAuthorize(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - //expectedResult := resource.Result{} + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") flag_allow_update, _ := fm.GetFlag("flag_allow_update") - //Assuming 1234 is the correct account pin + + // Set 1234 is the correct account pin accountPIN := "1234" - // Define session ID and mock data - sessionId := "session123" - typ := utils.DATA_ACCOUNT_PIN - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, st: mockState, } @@ -899,14 +904,13 @@ func TestAuthorize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create context with session ID ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte(accountPIN), nil) - - // Create a Handlers instance with the mock data store + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(accountPIN)) + if err != nil { + t.Fatal(err) + } // Call the method under test res, err := h.Authorize(ctx, "authorize", []byte(tt.input)) @@ -916,33 +920,25 @@ func TestAuthorize(t *testing.T) { //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 TestVerifyYob(t *testing.T) { fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } sessionId := "session123" // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) flag_incorrect_date_format, _ := fm.parser.GetFlag("flag_incorrect_date_format") ctx := context.WithValue(context.Background(), "SessionId", sessionId) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + accountService: mockAccountService, flagManager: fm.parser, st: mockState, } @@ -977,7 +973,6 @@ func TestVerifyYob(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Call the method under test res, err := h.VerifyYob(ctx, "verify_yob", []byte(tt.input)) @@ -986,37 +981,31 @@ func TestVerifyYob(t *testing.T) { //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 TestVerifyCreatePin(t *testing.T) { - fm, err := NewFlagManager(flagsPath) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - sessionId := "session123" // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) flag_valid_pin, _ := fm.parser.GetFlag("flag_valid_pin") flag_pin_mismatch, _ := fm.parser.GetFlag("flag_pin_mismatch") flag_pin_set, _ := fm.parser.GetFlag("flag_pin_set") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - //Assuming this was the first set PIN to verify against - firstSetPin := "1234" h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, st: mockState, } @@ -1043,16 +1032,12 @@ func TestVerifyCreatePin(t *testing.T) { }, } - typ := utils.DATA_TEMPORARY_VALUE - for _, tt := range tests { t.Run(tt.name, func(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) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte("1234")) + if err != nil { + t.Fatal(err) + } // Call the method under test res, err := h.VerifyCreatePin(ctx, "verify_create_pin", []byte(tt.input)) @@ -1062,36 +1047,32 @@ func TestVerifyCreatePin(t *testing.T) { //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 TestCheckAccountStatus(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - 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) - tests := []struct { name string - input []byte + publicKey []byte serverResponse *api.OKResponse - response *models.TrackStatusResponse expectedResult resource.Result }{ { - name: "Test when account is on the Sarafu network", - input: []byte("TrackingId1234"), + name: "Test when account is on the Sarafu network", + publicKey: []byte("TrackingId1234"), serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation succeeded", @@ -1099,54 +1080,14 @@ func TestCheckAccountStatus(t *testing.T) { "active": true, }, }, - 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: models.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_api_error, flag_account_pending}, }, }, { - name: "Test when the account is not yet on the sarafu network", - 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: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - }, + name: "Test when the account is not yet on the sarafu network", + publicKey: []byte("TrackingId1234"), serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation succeeded", @@ -1162,59 +1103,51 @@ func TestCheckAccountStatus(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } - status := tt.response.Result.Transaction.Status - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.input, nil) + err = store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(tt.publicKey)) + if err != nil { + t.Fatal(err) + } - 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() + mockAccountService.On("TrackAccountStatus", string(tt.publicKey)).Return(tt.serverResponse, nil) // Call the method under test - res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) + res, _ := h.CheckAccountStatus(ctx, "check_account_status", []byte("")) // 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 TestTransactionReset(t *testing.T) { - fm, err := NewFlagManager(flagsPath) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } + flag_invalid_recipient, _ := fm.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := fm.GetFlag("flag_invalid_recipient_with_invite") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - - sessionId := "session123" - - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { @@ -1233,9 +1166,6 @@ func TestTransactionReset(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, []byte("")).Return(nil) - // Call the method under test res, _ := h.TransactionReset(ctx, "transaction_reset", tt.input) @@ -1244,40 +1174,32 @@ func TestTransactionReset(t *testing.T) { //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 TestResetInvalidAmount(t *testing.T) { +func TestResetTransactionAmount(t *testing.T) { sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { name string - input []byte - status string expectedResult resource.Result }{ { @@ -1289,43 +1211,34 @@ func TestResetInvalidAmount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) - // Call the method under test - res, _ := h.ResetTransactionAmount(ctx, "transaction_reset_amount", tt.input) + res, _ := h.ResetTransactionAmount(ctx, "transaction_reset_amount", []byte("")) // 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 TestInitiateTransaction(t *testing.T) { sessionId := "254712345678" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } - account_authorized_flag, err := fm.parser.GetFlag("flag_account_authorized") + account_authorized_flag, _ := fm.parser.GetFlag("flag_account_authorized") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } @@ -1351,10 +1264,18 @@ func TestInitiateTransaction(t *testing.T) { } 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_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) + err := store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(tt.Amount)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(tt.Recipient)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(tt.ActiveSym)) + if err != nil { + t.Fatal(err) + } // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1364,31 +1285,25 @@ func TestInitiateTransaction(t *testing.T) { //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 TestQuit(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) + mockAccountService := new(mocks.MockAccountService) sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { @@ -1417,13 +1332,10 @@ func TestQuit(t *testing.T) { //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 TestIsValidPIN(t *testing.T) { tests := []struct { name string @@ -1479,21 +1391,22 @@ func TestIsValidPIN(t *testing.T) { func TestValidateAmount(t *testing.T) { fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } - flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") + + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { @@ -1533,11 +1446,10 @@ func TestValidateAmount(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // 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() + err := store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(tt.activeBal)) + if err != nil { + t.Fatal(err) + } // Call the method under test res, _ := h.ValidateAmount(ctx, "test_validate_amount", tt.input) @@ -1547,26 +1459,22 @@ func TestValidateAmount(t *testing.T) { // Assert the result matches the expected result assert.Equal(t, tt.expectedResult, res, "Expected result should match actual result") - - // Assert all expectations were met - mockDataStore.AssertExpectations(t) }) } } func TestValidateRecipient(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_invalid_recipient, _ := fm.parser.GetFlag("flag_invalid_recipient") - mockDataStore := new(mocks.MockUserDataStore) - - sessionId := "session123" - - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - if err != nil { log.Fatal(err) } + + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + flag_invalid_recipient, _ := fm.parser.GetFlag("flag_invalid_recipient") + // Define test cases tests := []struct { name string @@ -1590,13 +1498,10 @@ func TestValidateRecipient(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, tt.input).Return(nil) - - // Create the Handlers instance with the mock flag manager + // Create the Handlers instance h := &Handlers{ flagManager: fm.parser, - userdataStore: mockDataStore, + userdataStore: store, } // Call the method @@ -1605,14 +1510,16 @@ func TestValidateRecipient(t *testing.T) { if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestCheckBalance(t *testing.T) { + ctx, store := InitializeTestStore(t) + tests := []struct { name string sessionId string @@ -1624,7 +1531,7 @@ func TestCheckBalance(t *testing.T) { }{ { name: "User with active sym", - sessionId: "session456", + sessionId: "session123", publicKey: "0X98765432109", activeSym: "ETH", activeBal: "1.5", @@ -1635,18 +1542,22 @@ func TestCheckBalance(t *testing.T) { 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(ctx, "SessionId", tt.sessionId) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, 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) + err := store.WriteEntry(ctx, tt.sessionId, utils.DATA_ACTIVE_SYM, []byte(tt.activeSym)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, tt.sessionId, utils.DATA_ACTIVE_BAL, []byte(tt.activeBal)) + if err != nil { + t.Fatal(err) + } res, err := h.CheckBalance(ctx, "check_balance", []byte("")) @@ -1657,23 +1568,21 @@ func TestCheckBalance(t *testing.T) { assert.Equal(t, tt.expectedResult, res, "Result should match expected output") } - mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) }) } } func TestGetProfile(t *testing.T) { - sessionId := "session123" + ctx, store := InitializeTestStore(t) - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, st: mockState, } @@ -1697,7 +1606,7 @@ func TestGetProfile(t *testing.T) { }, }, { - name: "Test with with profile information in swa ", + 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", @@ -1723,22 +1632,21 @@ 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, "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).Maybe() + err := store.WriteEntry(ctx, sessionId, key, []byte(tt.profileInfo[index])) + if err != nil { + t.Fatal(err) + } } res, _ := h.GetProfileInfo(ctx, "get_profile_info", []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.result, "Result should contain profile information served back to user") - }) } } @@ -1749,12 +1657,10 @@ func TestVerifyNewPin(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_valid_pin, _ := fm.parser.GetFlag("flag_valid_pin") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, flagManager: fm.parser, - accountService: mockCreateAccountService, + accountService: mockAccountService, } ctx := context.WithValue(context.Background(), "SessionId", sessionId) @@ -1777,44 +1683,32 @@ func TestVerifyNewPin(t *testing.T) { FlagReset: []uint32{flag_valid_pin}, }, }, - { - name: "Test with invalid pin", - input: []byte("12345"), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_valid_pin}, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - //Call the function under test res, _ := h.VerifyNewPin(ctx, "verify_new_pin", tt.input) - // 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") - }) } - } func TestConfirmPin(t *testing.T) { sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, _ := NewFlagManager(flagsPath) flag_pin_mismatch, _ := fm.parser.GetFlag("flag_pin_mismatch") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, flagManager: fm.parser, - accountService: mockCreateAccountService, + accountService: mockAccountService, } - ctx := context.WithValue(context.Background(), "SessionId", sessionId) tests := []struct { name string @@ -1834,16 +1728,14 @@ func TestConfirmPin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) - - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.temporarypin)) + if err != nil { + t.Fatal(err) + } //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) - // 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") @@ -1861,7 +1753,14 @@ func TestFetchCustodialBalances(t *testing.T) { // Define test data sessionId := "session123" publicKey := "0X13242618721" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + err = store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } tests := []struct { name string @@ -1903,50 +1802,39 @@ func TestFetchCustodialBalances(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) - // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, flagManager: fm.parser, st: mockState, - accountService: mockCreateAccountService, + accountService: mockAccountService, } // 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) + mockAccountService.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") - + assert.Equal(t, res, tt.expectedResult, "Result should match expected result") }) } } func TestSetDefaultVoucher(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + 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 @@ -1954,7 +1842,7 @@ func TestSetDefaultVoucher(t *testing.T) { expectedResult resource.Result }{ { - name: "Test set default voucher when no active voucher exists", + name: "Test set default voucher when no active voucher is set", vouchersResp: &models.VoucherHoldingResponse{ Ok: true, Description: "Vouchers fetched successfully", @@ -1971,77 +1859,55 @@ func TestSetDefaultVoucher(t *testing.T) { }, expectedResult: resource.Result{}, }, - { - name: "Test no vouchers available", - vouchersResp: &models.VoucherHoldingResponse{ - Ok: true, - Description: "No vouchers available", - Result: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{}, - }, - }, - 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, + userdataStore: store, 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) + err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } 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")) + res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("")) 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) - sessionId := "session123" publicKey := "0X13242618721" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + spdb := InitializeTestSubPrefixDb(t, ctx) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, accountService: mockAccountService, prefixDb: spdb, } - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } mockVouchersResponse := &models.VoucherHoldingResponse{} mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{ @@ -2065,7 +1931,6 @@ func TestCheckVouchers(t *testing.T) { // assert that the data is stored correctly assert.Equal(t, expectedSym, voucherData) - mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) } @@ -2073,12 +1938,7 @@ func TestGetVoucherList(t *testing.T) { sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + spdb := InitializeTestSubPrefixDb(t, ctx) h := &Handlers{ prefixDb: spdb, @@ -2087,7 +1947,7 @@ func TestGetVoucherList(t *testing.T) { expectedSym := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err = spdb.Put(ctx, []byte("sym"), expectedSym) + err := spdb.Put(ctx, []byte("sym"), expectedSym) if err != nil { t.Fatal(err) } @@ -2103,20 +1963,15 @@ func TestViewVoucher(t *testing.T) { if err != nil { t.Logf(err.Error()) } - mockDataStore := new(mocks.MockUserDataStore) - + ctx, store := InitializeTestStore(t) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - db := memdb.NewMemDb() - err = db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + spdb := InitializeTestSubPrefixDb(t, ctx) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, flagManager: fm.parser, prefixDb: spdb, } @@ -2137,23 +1992,20 @@ func TestViewVoucher(t *testing.T) { } } - // Set up expectations for mockDataStore - expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce") - - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).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) + assert.Equal(t, res.Content, "SRF\n100") } func TestSetVoucher(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - + ctx, store := InitializeTestStore(t) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + ctx = context.WithValue(ctx, "SessionId", sessionId) + + h := &Handlers{ + userdataStore: store, + } // Define the temporary voucher data tempData := &dataserviceapi.TokenHoldings{ @@ -2163,33 +2015,16 @@ func TestSetVoucher(t *testing.T) { ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } - expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9") + expectedData := fmt.Sprintf("%s,%s,%s,%s", tempData.TokenSymbol, tempData.Balance, tempData.TokenDecimals, tempData.ContractAddress) - // Define the expected active entries - activeEntries := map[utils.DataTyp][]byte{ - utils.DATA_ACTIVE_SYM: []byte(tempData.TokenSymbol), - utils.DATA_ACTIVE_BAL: []byte(tempData.Balance), - utils.DATA_ACTIVE_DECIMAL: []byte(tempData.TokenDecimals), - utils.DATA_ACTIVE_ADDRESS: []byte(tempData.ContractAddress), + // store the expectedData + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)); err != nil { + t.Fatal(err) } - // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) - - // Mocking WriteEntry calls for setting active data - for key, value := range activeEntries { - mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) - } - - h := &Handlers{ - userdataStore: mockDataStore, - } - - res, err := h.SetVoucher(ctx, "someSym", []byte{}) + res, err := h.SetVoucher(ctx, "set_voucher", []byte{}) assert.NoError(t, err) assert.Equal(t, string(tempData.TokenSymbol), res.Content) - - mockDataStore.AssertExpectations(t) } From e29a24b376cff982367fe2c00470ee1af8eea5cd Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 15:46:46 +0000 Subject: [PATCH 252/261] Add missing models files --- common/hex.go | 13 +++++++++++++ internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 20 ++++++++++---------- internal/models/tokenresponse.go | 18 ++++++++++++++++++ internal/models/vouchersresponse.go | 14 ++++++++++++++ 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 internal/models/tokenresponse.go create mode 100644 internal/models/vouchersresponse.go diff --git a/common/hex.go b/common/hex.go index f5aa7ed..971ecf1 100644 --- a/common/hex.go +++ b/common/hex.go @@ -2,6 +2,7 @@ package common import ( "encoding/hex" + "strings" ) func NormalizeHex(s string) (string, error) { @@ -16,3 +17,15 @@ func NormalizeHex(s string) (string, error) { } return hex.EncodeToString(r), nil } + +func IsSameHex(left string, right string) bool { + bl, err := NormalizeHex(left) + if err != nil { + return false + } + br, err := NormalizeHex(left) + if err != nil { + return false + } + return strings.Compare(bl, br) == 0 +} diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 9afba13..7d8325c 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -10,6 +10,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" + "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/remote" ) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dc77660..a7e9d88 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -233,11 +233,11 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt return res, fmt.Errorf("missing session") } store := h.userdataStore - blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -307,12 +307,12 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } temporaryPin := string(input) - blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { return res, err } - err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) + err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { return res, err } @@ -841,15 +841,15 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } - err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, nil } @@ -874,7 +874,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } blockedNumber := string(input) - _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) + _, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY) if !isValidPhoneNumber(blockedNumber) { res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil @@ -888,7 +888,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input return res, err } } - err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) + err = store.WriteEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) if err != nil { return res, nil } @@ -1075,7 +1075,7 @@ func (h *Handlers) RetrieveBlockedNumber(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } store := h.userdataStore - blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedNumber, _ := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) res.Content = string(blockedNumber) 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"` +} diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go new file mode 100644 index 0000000..09b085d --- /dev/null +++ b/internal/models/vouchersresponse.go @@ -0,0 +1,14 @@ +package models + +import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" + +type VoucherHoldingResponse struct { + 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"` +} From 14bc11f4bd87574526b453339e4b079967216b83 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 16:38:29 +0000 Subject: [PATCH 253/261] Add transaction getter --- common/db.go | 2 +- common/vouchers.go | 9 +++++++++ remote/accountservice.go | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/common/db.go b/common/db.go index 58fd3a1..06d9607 100644 --- a/common/db.go +++ b/common/db.go @@ -30,7 +30,7 @@ const ( DATA_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS - + DATA_TRANSACTIONS ) func typToBytes(typ DataTyp) []byte { diff --git a/common/vouchers.go b/common/vouchers.go index 9ca6277..cd76060 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -37,6 +37,15 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { return data } +//func StoreVouchers(db storage.PrefixDb, data VoucherMetadata) { +// value, err := db.Put(ctx, []byte(key)) +// if err != nil { +// return nil, fmt.Errorf("failed to get %s: %v", key, err) +// } +// data[key] = string(value) +// } +//} + // GetVoucherData retrieves and matches voucher data func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { keys := []string{"sym", "bal", "deci", "addr"} diff --git a/remote/accountservice.go b/remote/accountservice.go index bc6a448..d0d7e9b 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -22,6 +22,7 @@ type AccountServiceInterface interface { CreateAccount(ctx context.Context) (*models.AccountResult, error) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) + FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) } type AccountService struct { @@ -100,13 +101,18 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes return &r, nil } -// FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint +// FetchVouchers retrieves the token holdings for a given public key from the data indexer API endpoint // Parameters: // - publicKey: The public key associated with the account. func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { var r []dataserviceapi.TokenHoldings - req, err := http.NewRequest("GET", config.VoucherHoldingsURL, nil) + ep, err := url.JoinPath(config.VoucherHoldingsURL, publicKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } @@ -119,6 +125,32 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return r, nil } + +// FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint +// Parameters: +// - publicKey: The public key associated with the account. +func (as *AccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { + var r []dataserviceapi.Last10TxResponse + + ep, err := url.JoinPath(config.VoucherTransfersURL, publicKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + + _, err = doDataRequest(ctx, req, r) + if err != nil { + return nil, err + } + + return r, nil +} + + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse From 3a8a5f40baa56612e8435387542bbd2fcf16368b Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 16:42:50 +0000 Subject: [PATCH 254/261] Add test service placeholders for fetchtransactions --- internal/testutil/mocks/servicemock.go | 5 +++++ internal/testutil/testservice/TestAccountService.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index bec48fd..78ee2b0 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -33,3 +33,8 @@ func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string args := m.Called(publicKey) return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) } + +func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { + args := m.Called(publicKey) + return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index a9a3085..3c104bd 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -42,3 +42,7 @@ func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey stri }, }, nil } + +func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { + return []dataserviceapi.Last10TxResponse{}, nil +} From 1d77ad98dc6e4d7be4a3f63561815c06d07039b2 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 23:33:52 +0000 Subject: [PATCH 255/261] Expose subprefix db --- common/storage.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 common/storage.go diff --git a/common/storage.go b/common/storage.go new file mode 100644 index 0000000..62a8a06 --- /dev/null +++ b/common/storage.go @@ -0,0 +1,13 @@ +package common + +import ( + "git.defalsify.org/vise.git/db" +) + +func StoreToDb(store *UserDataStore, prefix []byte) db.Db { + innerStore := store.Db + if pfx != nil { + innerStore = NewSubPrefixDb(innerStore, pfx) + } + return innerStore +} From dae12ac4989e40f64a48930b5348288c85af8369 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 23:41:08 +0000 Subject: [PATCH 256/261] Separate subprefix db export --- common/storage.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/common/storage.go b/common/storage.go index 62a8a06..145382d 100644 --- a/common/storage.go +++ b/common/storage.go @@ -2,12 +2,14 @@ package common import ( "git.defalsify.org/vise.git/db" + + "git.grassecon.net/urdt/ussd/internal/storage" ) -func StoreToDb(store *UserDataStore, prefix []byte) db.Db { - innerStore := store.Db - if pfx != nil { - innerStore = NewSubPrefixDb(innerStore, pfx) - } - return innerStore +func StoreToDb(store *UserDataStore) db.Db { + return store.Db +} + +func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { + return storage.NewSubPrefixDb(store.Db, pfx) } From a237b615f2ee3148146883e9267ac3c4f77f35e7 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Nov 2024 01:44:57 +0000 Subject: [PATCH 257/261] Export models package --- common/user_store.go | 1 + internal/handlers/ussd/menuhandler_test.go | 2 +- internal/testutil/mocks/servicemock.go | 2 +- internal/testutil/testservice/TestAccountService.go | 2 +- {internal/models => models}/accountresponse.go | 0 {internal/models => models}/balanceresponse.go | 0 {internal/models => models}/tokenresponse.go | 0 {internal/models => models}/trackstatusresponse.go | 0 {internal/models => models}/vouchersresponse.go | 0 remote/accountservice.go | 2 +- 10 files changed, 5 insertions(+), 4 deletions(-) rename {internal/models => models}/accountresponse.go (100%) rename {internal/models => models}/balanceresponse.go (100%) rename {internal/models => models}/tokenresponse.go (100%) rename {internal/models => models}/trackstatusresponse.go (100%) rename {internal/models => models}/vouchersresponse.go (100%) diff --git a/common/user_store.go b/common/user_store.go index 4b8409f..29796e2 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -25,6 +25,7 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ } // WriteEntry adds an entry to the userdata store. +// BUG: this uses sessionId twice func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 86c905d..74e9f53 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -14,7 +14,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/models" + "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index 78ee2b0..a24e459 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -3,7 +3,7 @@ package mocks import ( "context" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/stretchr/testify/mock" ) diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 3c104bd..21f7a63 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) diff --git a/internal/models/accountresponse.go b/models/accountresponse.go similarity index 100% rename from internal/models/accountresponse.go rename to models/accountresponse.go diff --git a/internal/models/balanceresponse.go b/models/balanceresponse.go similarity index 100% rename from internal/models/balanceresponse.go rename to models/balanceresponse.go diff --git a/internal/models/tokenresponse.go b/models/tokenresponse.go similarity index 100% rename from internal/models/tokenresponse.go rename to models/tokenresponse.go diff --git a/internal/models/trackstatusresponse.go b/models/trackstatusresponse.go similarity index 100% rename from internal/models/trackstatusresponse.go rename to models/trackstatusresponse.go diff --git a/internal/models/vouchersresponse.go b/models/vouchersresponse.go similarity index 100% rename from internal/models/vouchersresponse.go rename to models/vouchersresponse.go diff --git a/remote/accountservice.go b/remote/accountservice.go index d0d7e9b..402add5 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -11,7 +11,7 @@ import ( dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" ) var ( From 0506a8c452f1c5daab3383d4c3248a0b0780dbdf Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Nov 2024 14:34:26 +0000 Subject: [PATCH 258/261] Add voucherdata endpoint --- common/db.go | 6 ++++++ common/vouchers.go | 2 ++ config/config.go | 3 +++ models/vouchersresponse.go | 17 ++++++++++++----- remote/accountservice.go | 21 +++++++++++++++++++++ 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/common/db.go b/common/db.go index 06d9607..91891d9 100644 --- a/common/db.go +++ b/common/db.go @@ -2,6 +2,8 @@ package common import ( "encoding/binary" + + "git.defalsify.org/vise.git/logging" ) type DataTyp uint16 @@ -33,6 +35,10 @@ const ( DATA_TRANSACTIONS ) +var ( + logg = logging.NewVanilla().WithDomain("urdt-common") +) + func typToBytes(typ DataTyp) []byte { var b [2]byte binary.BigEndian.PutUint16(b[:], uint16(typ)) diff --git a/common/vouchers.go b/common/vouchers.go index cd76060..2fed043 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -84,6 +84,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, decList := strings.Split(decimals, "\n") addrList := strings.Split(addresses, "\n") + logg.Tracef("found" , "symlist", symList, "syms", symbols, "input", input) for i, sym := range symList { parts := strings.SplitN(sym, ":", 2) @@ -136,6 +137,7 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str // 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 *dataserviceapi.TokenHoldings) error { + logg.TraceCtxf(ctx, "dtal", "data", data) // Active voucher data entries activeEntries := map[DataTyp][]byte{ DATA_ACTIVE_SYM: []byte(data.TokenSymbol), diff --git a/config/config.go b/config/config.go index a5c89a7..fbf518b 100644 --- a/config/config.go +++ b/config/config.go @@ -13,6 +13,7 @@ const ( trackPath = "/api/v2/account/status" voucherHoldingsPathPrefix = "/api/v1/holdings" voucherTransfersPathPrefix = "/api/v1/transfers/last10" + voucherDataPathPrefix = "/api/v1/token" ) var ( @@ -29,6 +30,7 @@ var ( TrackURL string VoucherHoldingsURL string VoucherTransfersURL string + VoucherDataURL string ) func setBase() error { @@ -62,6 +64,7 @@ func LoadConfig() error { TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) + VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) return nil } diff --git a/models/vouchersresponse.go b/models/vouchersresponse.go index 09b085d..8cf3ec6 100644 --- a/models/vouchersresponse.go +++ b/models/vouchersresponse.go @@ -2,13 +2,20 @@ package models import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" -type VoucherHoldingResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` - Result VoucherResult `json:"result"` -} +//type VoucherHoldingResponse struct { +// 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"` } + +type VoucherDataResult struct { + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + SinkAddress string `json:"sinkAddress"` +} diff --git a/remote/accountservice.go b/remote/accountservice.go index 402add5..73052f6 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -23,6 +23,7 @@ type AccountServiceInterface interface { TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) + VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) } type AccountService struct { @@ -151,6 +152,26 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin } +// VoucherData retrieves voucher metadata from the data indexer API endpoint. +// Parameters: +// - address: The voucher address. +func (as *AccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { + var voucherDataResult models.VoucherDataResult + + ep, err := url.JoinPath(config.VoucherDataURL, address) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + + _, err = doCustodialRequest(ctx, req, &voucherDataResult) + return &voucherDataResult, err +} + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse From 7189235bee57b4fc703c5d7a2f43f0b4a660c397 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Nov 2024 15:14:17 +0000 Subject: [PATCH 259/261] Remove unused data type --- common/db.go | 1 - 1 file changed, 1 deletion(-) diff --git a/common/db.go b/common/db.go index 91891d9..1992476 100644 --- a/common/db.go +++ b/common/db.go @@ -25,7 +25,6 @@ const ( DATA_RECIPIENT DATA_AMOUNT DATA_TEMPORARY_VALUE - DATA_VOUCHER_LIST DATA_ACTIVE_SYM DATA_ACTIVE_BAL DATA_BLOCKED_NUMBER From 2c30ccc4058f821f7996442963defced49e4146e Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 4 Nov 2024 02:23:30 +0000 Subject: [PATCH 260/261] Add voucherdata call to test accountservices --- common/storage.go | 39 ++++++++++++++++++- internal/testutil/mocks/servicemock.go | 5 +++ .../testservice/TestAccountService.go | 4 ++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/common/storage.go b/common/storage.go index 145382d..dff4774 100644 --- a/common/storage.go +++ b/common/storage.go @@ -1,8 +1,12 @@ package common import ( - "git.defalsify.org/vise.git/db" + "context" + "errors" + "git.defalsify.org/vise.git/db" + "git.defalsify.org/vise.git/resource" + "git.defalsify.org/vise.git/persist" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -13,3 +17,36 @@ func StoreToDb(store *UserDataStore) db.Db { func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { return storage.NewSubPrefixDb(store.Db, pfx) } + +type StorageServices interface { + GetPersister(ctx context.Context) (*persist.Persister, error) + GetUserdataDb(ctx context.Context) (db.Db, error) + GetResource(ctx context.Context) (resource.Resource, error) + EnsureDbDir() error +} + +type StorageService struct { + svc *storage.MenuStorageService +} + +func NewStorageService(dbDir string) *StorageService { + return &StorageService{ + svc: storage.NewMenuStorageService(dbDir, ""), + } +} + +func(ss *StorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { + return ss.svc.GetPersister(ctx) +} + +func(ss *StorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { + return ss.svc.GetUserdataDb(ctx) +} + +func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, error) { + return nil, errors.New("not implemented") +} + +func(ss *StorageService) EnsureDbDir() error { + return ss.svc.EnsureDbDir() +} diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index a24e459..76803ba 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -38,3 +38,8 @@ func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey st args := m.Called(publicKey) return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) } + +func(m MockAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { + args := m.Called(address) + return args.Get(0).(*models.VoucherDataResult), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 21f7a63..8752d6f 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -46,3 +46,7 @@ func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey stri func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { return []dataserviceapi.Last10TxResponse{}, nil } + +func(m TestAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { + return &models.VoucherDataResult{}, nil +} From 88b50c5dd77852b2cd88097973162e1090cc2e89 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 4 Nov 2024 15:52:03 +0300 Subject: [PATCH 261/261] Remove admin number defination in env example --- .env.example | 4 --- internal/handlers/ussd/menuhandler.go | 3 +-- internal/handlers/ussd/menuhandler_test.go | 31 +++++++++++----------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/.env.example b/.env.example index 4d86f8b..ab370a7 100644 --- a/.env.example +++ b/.env.example @@ -16,7 +16,3 @@ 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 - - -#numbers with privileges to reset others pin -ADMIN_NUMBERS=254051722XXX,255012221XXX \ No newline at end of file diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a7e9d88..6c1917d 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -20,8 +20,8 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/common" - "git.grassecon.net/urdt/ussd/remote" "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/remote" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -1390,7 +1390,6 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re return res, fmt.Errorf("missing session") } - // Get temporary data tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 2f2e94b..f4c9f7c 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -12,10 +12,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/models" "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" + "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/common" "github.com/alecthomas/assert/v2" @@ -122,7 +122,7 @@ func TestCreateAccount(t *testing.T) { name: "Test account creation success", serverResponse: &models.AccountResult{ TrackingId: "1234567890", - PublicKey: "0xD3adB33f", + PublicKey: "0xD3adB33f", }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_created}, @@ -176,7 +176,7 @@ func TestSaveFirstname(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -215,7 +215,7 @@ func TestSaveFamilyname(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -254,7 +254,7 @@ func TestSaveYoB(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -265,7 +265,7 @@ func TestSaveYoB(t *testing.T) { // Define test data yob := "1980" - + if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { t.Fatal(err) } @@ -293,7 +293,7 @@ func TestSaveLocation(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -304,7 +304,7 @@ func TestSaveLocation(t *testing.T) { // Define test data location := "Kilifi" - + if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { t.Fatal(err) } @@ -332,7 +332,7 @@ func TestSaveOfferings(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -371,7 +371,7 @@ func TestSaveGender(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -1746,9 +1746,9 @@ func TestFetchCustodialBalances(t *testing.T) { } tests := []struct { - name string + name string balanceResponse *models.BalanceResult - expectedResult resource.Result + expectedResult resource.Result }{ { name: "Test when fetch custodial balances is not a success", @@ -1807,16 +1807,15 @@ func TestSetDefaultVoucher(t *testing.T) { expectedResult resource.Result }{ { - name: "Test no vouchers available", - vouchersResp: []dataserviceapi.TokenHoldings { - }, + name: "Test no vouchers available", + vouchersResp: []dataserviceapi.TokenHoldings{}, expectedResult: resource.Result{ FlagSet: []uint32{flag_no_active_voucher}, }, }, { name: "Test set default voucher when no active voucher is set", - vouchersResp: []dataserviceapi.TokenHoldings { + vouchersResp: []dataserviceapi.TokenHoldings{ dataserviceapi.TokenHoldings{ ContractAddress: "0x123", TokenSymbol: "TOKEN1",