diff --git a/.gitignore b/.gitignore index 54ad3ff..ddccccf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ go.work* **/*/*.bin **/*/.state/ cmd/.state/ +id_* +*.gdbm diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index b733efe..3dffe16 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -88,7 +88,6 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo) ls.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob) ls.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob) - ls.DbRs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit) ls.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction) ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin) ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4fc6dbb..f41d955 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -118,16 +118,13 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r var res resource.Result symbol, _ := h.st.Where() + code := strings.Split(symbol, "_")[1] - switch symbol { - case "set_default": - res.FlagSet = append(res.FlagSet, state.FLAG_LANG) - res.Content = "eng" - case "set_swa": - res.FlagSet = append(res.FlagSet, state.FLAG_LANG) - res.Content = "swa" - default: + if !utils.IsValidISO639(code) { + return res, nil } + res.FlagSet = append(res.FlagSet, state.FLAG_LANG) + res.Content = code languageSetFlag, err := h.flagManager.GetFlag("flag_language_set") if err != nil { @@ -319,32 +316,6 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt return res, nil } -// SetResetSingleEdit sets and resets flags to allow gradual editing of profile information. -func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - - menuOption := string(input) - - flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") - flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit") - - switch menuOption { - case "2": - res.FlagReset = append(res.FlagReset, flag_allow_update) - res.FlagSet = append(res.FlagSet, flag_single_edit) - case "3": - res.FlagReset = append(res.FlagReset, flag_allow_update) - res.FlagSet = append(res.FlagSet, flag_single_edit) - case "4": - res.FlagReset = append(res.FlagReset, flag_allow_update) - res.FlagSet = append(res.FlagSet, flag_single_edit) - default: - res.FlagReset = append(res.FlagReset, flag_single_edit) - } - - 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 // to access the main menu @@ -475,6 +446,7 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( // SaveGender updates the gender in the gdbm with the provided input. func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (resource.Result, error) { + symbol, _ := h.st.Where() var res resource.Result var err error sessionId, ok := ctx.Value("SessionId").(string) @@ -482,21 +454,11 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re return res, fmt.Errorf("missing session") } - if len(input) > 0 { - gender := string(input) - switch gender { - case "1": - gender = "Male" - case "2": - gender = "Female" - case "3": - gender = "Unspecified" - } - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) - if err != nil { - 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 } return res, nil diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 64d0b0e..4a76f26 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -9,6 +9,7 @@ import ( "testing" "git.defalsify.org/vise.git/db" + "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" @@ -16,6 +17,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" testdataloader "github.com/peteole/testdata-loader" + "github.com/stretchr/testify/require" ) var ( @@ -94,6 +96,25 @@ func TestCreateAccount(t *testing.T) { mockDataStore.AssertExpectations(t) } +func TestWithPersister(t *testing.T) { + // Test case: Setting a persister + h := &Handlers{} + p := &persist.Persister{} + + result := h.WithPersister(p) + + assert.Equal(t, p, h.pe, "The persister should be set correctly.") + assert.Equal(t, h, result, "The returned handler should be the same instance.") +} + +func TestWithPersister_PanicWhenAlreadySet(t *testing.T) { + // Test case: Panic on multiple calls + h := &Handlers{pe: &persist.Persister{}} + require.Panics(t, func() { + h.WithPersister(&persist.Persister{}) + }, "Should panic when trying to set a persister again.") +} + func TestSaveFirstname(t *testing.T) { // Create a new instance of MockMyDataStore mockStore := new(mocks.MockUserDataStore) @@ -295,6 +316,7 @@ func TestSaveOfferings(t *testing.T) { func TestSaveGender(t *testing.T) { // Create a new instance of MockMyDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) // Define the session ID and context sessionId := "session123" @@ -302,34 +324,32 @@ func TestSaveGender(t *testing.T) { // Define test cases tests := []struct { - name string - input []byte - expectedGender string - expectCall bool + name string + input []byte + expectedGender string + expectCall bool + executingSymbol string }{ { - name: "Valid Male Input", - input: []byte("1"), - expectedGender: "Male", - expectCall: true, + name: "Valid Male Input", + input: []byte("1"), + expectedGender: "male", + executingSymbol: "set_male", + expectCall: true, }, { - name: "Valid Female Input", - input: []byte("2"), - expectedGender: "Female", - expectCall: true, + name: "Valid Female Input", + input: []byte("2"), + expectedGender: "female", + executingSymbol: "set_female", + expectCall: true, }, { - name: "Valid Unspecified Input", - input: []byte("3"), - expectedGender: "Unspecified", - expectCall: true, - }, - { - name: "Empty Input", - input: []byte(""), - expectedGender: "", - expectCall: false, + name: "Valid Unspecified Input", + input: []byte("3"), + executingSymbol: "set_unspecified", + expectedGender: "unspecified", + expectCall: true, }, } @@ -342,14 +362,15 @@ func TestSaveGender(t *testing.T) { } else { mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_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, } // Call the method - _, err := h.SaveGender(ctx, "someSym", tt.input) + _, err := h.SaveGender(ctx, "save_gender", tt.input) // Assert no error assert.NoError(t, err) @@ -538,13 +559,13 @@ func TestSetLanguage(t *testing.T) { } // Define test cases tests := []struct { - name string - execPath []string - expectedResult resource.Result + name string + execPath []string + expectedResult resource.Result }{ { name: "Set Default Language (English)", - execPath: []string{"set_default"}, + execPath: []string{"set_eng"}, expectedResult: resource.Result{ FlagSet: []uint32{state.FLAG_LANG, 8}, Content: "eng", @@ -558,13 +579,6 @@ func TestSetLanguage(t *testing.T) { Content: "swa", }, }, - { - name: "Unhandled path", - execPath: []string{""}, - expectedResult: resource.Result{ - FlagSet: []uint32{8}, - }, - }, } for _, tt := range tests { @@ -592,76 +606,6 @@ func TestSetLanguage(t *testing.T) { }) } } - -func TestSetResetSingleEdit(t *testing.T) { - fm, err := NewFlagManager(flagsPath) - - flag_allow_update, _ := fm.parser.GetFlag("flag_allow_update") - flag_single_edit, _ := fm.parser.GetFlag("flag_single_edit") - - if err != nil { - log.Fatal(err) - } - // Define test cases - tests := []struct { - name string - input []byte - expectedResult resource.Result - }{ - { - name: "Set single Edit", - input: []byte("2"), - expectedResult: resource.Result{ - FlagSet: []uint32{flag_single_edit}, - FlagReset: []uint32{flag_allow_update}, - }, - }, - { - name: "Set single Edit", - input: []byte("3"), - expectedResult: resource.Result{ - FlagSet: []uint32{flag_single_edit}, - FlagReset: []uint32{flag_allow_update}, - }, - }, - { - name: "Set single edit", - input: []byte("4"), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_allow_update}, - FlagSet: []uint32{flag_single_edit}, - }, - }, - { - name: "No single edit set", - input: []byte("1"), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_single_edit}, - }, - }, - } - - 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, - } - - // Call the method - res, err := h.SetResetSingleEdit(context.Background(), "set_reset_single_edit", 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 match reset edit") - - }) - } -} - func TestResetAllowUpdate(t *testing.T) { fm, err := NewFlagManager(flagsPath) @@ -1483,7 +1427,7 @@ func TestValidateAmount(t *testing.T) { if err != nil { t.Logf(err.Error()) } - //flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") + flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) @@ -1512,26 +1456,26 @@ func TestValidateAmount(t *testing.T) { Content: "0.001", }, }, - // { - // name: "Test with amount larger than balance", - // input: []byte("0.02"), - // balance: "0.003 CELO", - // publicKey: []byte("0xrqeqrequuq"), - // expectedResult: resource.Result{ - // FlagSet: []uint32{flag_invalid_amount}, - // Content: "0.02", - // }, - // }, - // { - // name: "Test with invalid amount", - // input: []byte("0.02ms"), - // balance: "0.003 CELO", - // publicKey: []byte("0xrqeqrequuq"), - // expectedResult: resource.Result{ - // FlagSet: []uint32{flag_invalid_amount}, - // Content: "0.02ms", - // }, - // }, + { + name: "Test with amount larger than balance", + input: []byte("0.02"), + balance: "0.003 CELO", + publicKey: []byte("0xrqeqrequuq"), + expectedResult: resource.Result{ + FlagSet: []uint32{flag_invalid_amount}, + Content: "0.02", + }, + }, + { + name: "Test with invalid amount", + input: []byte("0.02ms"), + balance: "0.003 CELO", + publicKey: []byte("0xrqeqrequuq"), + expectedResult: resource.Result{ + FlagSet: []uint32{flag_invalid_amount}, + Content: "0.02ms", + }, + }, } for _, tt := range tests { @@ -1539,7 +1483,7 @@ func TestValidateAmount(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) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil) + 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) @@ -1651,6 +1595,7 @@ func TestGetProfile(t *testing.T) { mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) + h := &Handlers{ userdataStore: mockDataStore, accountService: mockCreateAccountService, diff --git a/internal/utils/isocode.go b/internal/utils/isocode.go new file mode 100644 index 0000000..3bdfbeb --- /dev/null +++ b/internal/utils/isocode.go @@ -0,0 +1,11 @@ +package utils + +var isoCodes = map[string]bool{ + "eng": true, // English + "swa": true, // Swahili + +} + +func IsValidISO639(code string) bool { + return isoCodes[code] +} diff --git a/services/registration/_catch b/services/registration/_catch new file mode 100644 index 0000000..e81b8e9 --- /dev/null +++ b/services/registration/_catch @@ -0,0 +1 @@ +Something went wrong.Please try again \ No newline at end of file diff --git a/services/registration/_catch.vis b/services/registration/_catch.vis new file mode 100644 index 0000000..72e55ad --- /dev/null +++ b/services/registration/_catch.vis @@ -0,0 +1 @@ +HALT diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index 4116809..277f330 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -12,8 +12,6 @@ MOUT view 7 MOUT back 0 HALT INCMP my_account 0 -LOAD set_reset_single_edit 0 -RELOAD set_reset_single_edit INCMP enter_name 1 INCMP enter_familyname 2 INCMP select_gender 3 diff --git a/services/registration/select_gender.vis b/services/registration/select_gender.vis index c4e60d1..1082cef 100644 --- a/services/registration/select_gender.vis +++ b/services/registration/select_gender.vis @@ -1,11 +1,15 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH profile_update_success flag_allow_update 1 -LOAD save_gender 0 MOUT male 1 MOUT female 2 MOUT unspecified 3 MOUT back 0 HALT -RELOAD save_gender INCMP _ 0 -INCMP pin_entry * +INCMP set_male 1 +INCMP set_female 2 +INCMP set_unspecified 3 + + + + diff --git a/services/registration/select_language.vis b/services/registration/select_language.vis index aa83e0c..54f08e9 100644 --- a/services/registration/select_language.vis +++ b/services/registration/select_language.vis @@ -1,6 +1,6 @@ MOUT english 0 MOUT kiswahili 1 HALT -INCMP set_default 0 +INCMP set_eng 0 INCMP set_swa 1 INCMP . * diff --git a/services/registration/set_default.vis b/services/registration/set_eng.vis similarity index 100% rename from services/registration/set_default.vis rename to services/registration/set_eng.vis diff --git a/services/registration/set_female.vis b/services/registration/set_female.vis new file mode 100644 index 0000000..723b080 --- /dev/null +++ b/services/registration/set_female.vis @@ -0,0 +1,4 @@ +LOAD save_gender 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH profile_update_success flag_allow_update 1 +MOVE pin_entry diff --git a/services/registration/set_male.vis b/services/registration/set_male.vis new file mode 100644 index 0000000..723b080 --- /dev/null +++ b/services/registration/set_male.vis @@ -0,0 +1,4 @@ +LOAD save_gender 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH profile_update_success flag_allow_update 1 +MOVE pin_entry diff --git a/services/registration/set_unspecified.vis b/services/registration/set_unspecified.vis new file mode 100644 index 0000000..723b080 --- /dev/null +++ b/services/registration/set_unspecified.vis @@ -0,0 +1,4 @@ +LOAD save_gender 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH profile_update_success flag_allow_update 1 +MOVE pin_entry