WIP: wip-menu-select-fix #72

Closed
carlos wants to merge 12 commits from wip-menu-select-fix into master
8 changed files with 209 additions and 89 deletions

View File

@ -117,17 +117,14 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource
func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sym, _ = h.st.Where()
symbol, _ := h.st.Where()
code := strings.Split(symbol, "_")[1]
switch sym {
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 {
@ -282,26 +279,8 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt
// 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)
}
res.FlagReset = append(res.FlagReset, flag_single_edit)
return res, nil
}
@ -377,7 +356,6 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte)
if !ok {
return res, fmt.Errorf("missing session")
}
if len(input) > 0 {
familyName := string(input)
store := h.userdataStore
@ -400,7 +378,15 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou
if !ok {
return res, fmt.Errorf("missing session")
}
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit")
execPath := utils.GetSingleEditExecutionPath("save_yob")
isSingleEdit := utils.MatchNavigationPath(execPath, h.st.ExecPath)
if isSingleEdit {
res.FlagReset = append(res.FlagReset, flag_allow_update)
res.FlagSet = append(res.FlagSet, flag_single_edit)
}
if len(input) == 4 {
yob := string(input)
store := h.userdataStore
@ -422,6 +408,16 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) (
return res, fmt.Errorf("missing session")
}
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit")
execPath := utils.GetSingleEditExecutionPath("save_location")
isSingleEdit := utils.MatchNavigationPath(execPath, h.st.ExecPath)
if isSingleEdit {
res.FlagReset = append(res.FlagReset, flag_allow_update)
res.FlagSet = append(res.FlagSet, flag_single_edit)
}
if len(input) > 0 {
location := string(input)
store := h.userdataStore
@ -442,16 +438,38 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re
if !ok {
return res, fmt.Errorf("missing session")
}
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit")
execPath := utils.GetSingleEditExecutionPath("select_gender")
isSingleEdit := utils.MatchNavigationPath(execPath, h.st.ExecPath)
if isSingleEdit {
res.FlagReset = append(res.FlagReset, flag_allow_update)
res.FlagSet = append(res.FlagSet, flag_single_edit)
}
code := codeFromCtx(ctx)
if len(input) > 0 {
gender := string(input)
switch gender {
case "1":
gender = "Male"
if code == "swa" {
gender = "Mwanaume"
} else {
gender = "Male"
}
case "2":
gender = "Female"
if code == "swa" {
gender = "Mwanamke"
} else {
gender = "Female"
}
case "3":
gender = "Unspecified"
if code == "swa" {
gender = "Haijabainishwa"
} else {
gender = "Unspecified"
}
}
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender))
@ -640,7 +658,6 @@ func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) (
return res, nil
}
// VerifyYob verifies the length of the given input
func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
@ -988,13 +1005,20 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []
// 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")
}
code := h.st.Language.Code
// Default value when an entry is not found
defaultValue := "Not Provided"
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 {
@ -1031,12 +1055,22 @@ 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 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
}

View File

@ -9,6 +9,7 @@ import (
"testing"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/lang"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state"
"git.grassecon.net/urdt/ussd/internal/mocks"
@ -349,7 +350,7 @@ func TestSaveGender(t *testing.T) {
}
// 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 +539,15 @@ func TestSetLanguage(t *testing.T) {
}
// Define test cases
tests := []struct {
name string
execPath []string
expectedResult resource.Result
name string
execPath []string
expectedResult resource.Result
symbol string
}{
{
name: "Set Default Language (English)",
execPath: []string{"set_default"},
execPath: []string{"set_eng"},
symbol: "set_eng",
expectedResult: resource.Result{
FlagSet: []uint32{state.FLAG_LANG, 8},
Content: "eng",
@ -552,19 +555,20 @@ func TestSetLanguage(t *testing.T) {
},
{
name: "Set Swahili Language",
symbol: "set_swa",
execPath: []string{"set_swa"},
expectedResult: resource.Result{
FlagSet: []uint32{state.FLAG_LANG, 8},
Content: "swa",
},
},
{
name: "Unhandled path",
execPath: []string{""},
expectedResult: resource.Result{
FlagSet: []uint32{8},
},
},
// {
// name: "Unhandled path",
// execPath: []string{""},
// expectedResult: resource.Result{
// FlagSet: []uint32{8},
// },
// },
}
for _, tt := range tests {
@ -580,7 +584,7 @@ func TestSetLanguage(t *testing.T) {
}
// Call the method
res, err := h.SetLanguage(context.Background(), "set_language", nil)
res, err := h.SetLanguage(context.Background(), tt.symbol, nil)
if err != nil {
t.Error(err)
@ -1101,18 +1105,26 @@ func TestCheckAccountStatus(t *testing.T) {
FlagReset: []uint32{flag_account_pending},
},
},
{
name: "Test when account status is not a success",
input: []byte("TrackingId12"),
status: "REVERTED",
expectedResult: resource.Result{
FlagSet: []uint32{flag_account_success},
FlagReset: []uint32{flag_account_pending},
},
},
}
typ := utils.DATA_TRACKING_ID
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.status, nil)
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(tt.status)).Return(nil).Maybe()
// Define expected interactions with the mock
mockDataStore.On("ReadEntry", ctx, sessionId, typ).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)
// Call the method under test
res, _ := h.CheckAccountStatus(ctx, "check_status", tt.input)
@ -1480,7 +1492,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)
@ -1509,26 +1521,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 {
@ -1536,7 +1548,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)
@ -1630,7 +1642,6 @@ func TestCheckBalance(t *testing.T) {
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
//flagManager: fm.parser,
}
//mock call operations
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
@ -1648,22 +1659,34 @@ func TestGetProfile(t *testing.T) {
mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService)
mockState := state.NewState(16)
// Set the ExecPath
ll := &lang.Language{
Code: "swa",
}
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
// st: mockState,
}
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
ctx = context.WithValue(ctx, "Language", ll)
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",
@ -1671,12 +1694,39 @@ 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},
profileInfo: []string{"Doee", "John", "Jinsia", "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", "Jinsia", "Bananas", "Kilifi", "1976"},
languageCode: "kamba",
result: resource.Result{
Content: fmt.Sprintf(
"Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n",
"John Doee", "Male", "48", "Kilifi", "Bananas",
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for index, key := range tt.keys {
mockDataStore.On("ReadEntry", ctx, sessionId, key).Return([]byte(tt.profileInfo[index]), nil)
}
mockState.SetLanguage(tt.languageCode)
h.st = mockState
res, _ := h.GetProfileInfo(ctx, "get_profile_info", []byte(""))
// Assert that expectations were met

View File

@ -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]
}

View File

@ -0,0 +1,24 @@
package utils
func MatchNavigationPath(a, b []string) bool {
if len(a) != len(b) {
return false
}
//Check if the navigation path matches with single edit
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func GetSingleEditExecutionPath(key string) []string {
paths := make(map[string][]string)
paths["select_gender"] = []string{"root", "main", "my_account", "edit_profile", "select_gender"}
paths["save_location"] = []string{"root", "main", "my_account", "edit_profile", "enter_location"}
paths["save_yob"] = []string{"root", "main", "my_account", "edit_profile", "enter_yob"}
return paths[key]
}

View File

@ -1,6 +1,6 @@
MOUT english 0
MOUT kiswahili 1
HALT
INCMP set_default 0
INCMP set_eng 0
INCMP set_swa 1
INCMP . *

View File

@ -1 +1 @@
Akaunti imeupdatiwa
Ombi lako la kuweka wasifu limefanikiwa

View File

@ -0,0 +1 @@
Angalia Wasifu