From 8313ca35090e8b3a6608ebf6a7b91db58eeb64d1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:29:30 +0300 Subject: [PATCH 01/24] update affected vis and template files --- services/registration/confirm_pin_change | 2 +- services/registration/confirm_pin_change.vis | 3 +-- services/registration/edit_profile.vis | 2 ++ services/registration/language_changed.vis | 1 + services/registration/new_pin | 2 +- services/registration/new_pin.vis | 11 +++++------ services/registration/new_pin_swa | 3 +-- services/registration/old_pin | 2 +- services/registration/old_pin.vis | 6 ++++-- services/registration/pin_management.vis | 2 +- services/registration/pin_reset_mismatch.vis | 1 - services/registration/pin_reset_success | 2 +- services/registration/pin_reset_success.vis | 4 +--- services/registration/profile_update_success | 2 +- 14 files changed, 21 insertions(+), 22 deletions(-) diff --git a/services/registration/confirm_pin_change b/services/registration/confirm_pin_change index 398a827..2e1b83e 100644 --- a/services/registration/confirm_pin_change +++ b/services/registration/confirm_pin_change @@ -1 +1 @@ -Confirm your new PIN: +Confirm your new PIN: \ No newline at end of file diff --git a/services/registration/confirm_pin_change.vis b/services/registration/confirm_pin_change.vis index 7691e01..5f5b1f7 100644 --- a/services/registration/confirm_pin_change.vis +++ b/services/registration/confirm_pin_change.vis @@ -2,6 +2,5 @@ CATCH invalid_pin flag_valid_pin 0 MOUT back 0 HALT INCMP _ 0 -INCMP * pin_reset_success - +INCMP pin_reset_success * diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index 9d45ec9..a39fff4 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 @@ -18,3 +19,4 @@ INCMP enter_yob 4 INCMP enter_location 5 INCMP enter_offerings 6 INCMP view_profile 7 + diff --git a/services/registration/language_changed.vis b/services/registration/language_changed.vis index 832ef22..109b237 100644 --- a/services/registration/language_changed.vis +++ b/services/registration/language_changed.vis @@ -1,3 +1,4 @@ +RELOAD reset_account_authorized MOUT back 0 MOUT quit 9 HALT diff --git a/services/registration/new_pin b/services/registration/new_pin index bae2814..30536ea 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: \ No newline at end of file diff --git a/services/registration/new_pin.vis b/services/registration/new_pin.vis index 29013a9..804a5f6 100644 --- a/services/registration/new_pin.vis +++ b/services/registration/new_pin.vis @@ -1,13 +1,12 @@ -LOAD authorize_account 12 -RELOAD authorize_account -CATCH incorrect_pin flag_incorrect_pin 1 -CATCH old_pin flag_allow_update 0 +CATCH _ flag_allow_update 0 MOUT back 0 HALT INCMP _ 0 LOAD save_temporary_pin 6 -LOAD verify_new_pin 0 RELOAD save_temporary_pin +LOAD verify_new_pin 8 RELOAD verify_new_pin -INCMP * confirm_pin_change +CATCH incorrect_pin flag_incorrect_pin 1 +INCMP confirm_pin_change * + diff --git a/services/registration/new_pin_swa b/services/registration/new_pin_swa index 1ec32d9..a2dc212 100644 --- a/services/registration/new_pin_swa +++ b/services/registration/new_pin_swa @@ -1,2 +1 @@ -Weka PIN mpya ya nne nambari: - +Weka PIN mpya ya nne nambari: \ No newline at end of file diff --git a/services/registration/old_pin b/services/registration/old_pin index 2c64d42..798d1ce 100644 --- a/services/registration/old_pin +++ b/services/registration/old_pin @@ -1 +1 @@ -Enter your old PIN +Enter your old PIN \ No newline at end of file diff --git a/services/registration/old_pin.vis b/services/registration/old_pin.vis index 1e99f4f..52379eb 100644 --- a/services/registration/old_pin.vis +++ b/services/registration/old_pin.vis @@ -1,7 +1,9 @@ LOAD reset_allow_update 0 +RELOAD reset_allow_update MOUT back 0 HALT -RELOAD reset_allow_update +LOAD authorize_account 12 +RELOAD authorize_account +CATCH incorrect_pin flag_incorrect_pin 1 INCMP _ 0 INCMP new_pin * - diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index 3b33dad..30f66bc 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -4,5 +4,5 @@ MOUT guard_pin 3 MOUT back 0 HALT INCMP _ 0 -INCMP old_pin 1 +INCMP old_pin 1 diff --git a/services/registration/pin_reset_mismatch.vis b/services/registration/pin_reset_mismatch.vis index 5dc7e7c..b0d4a44 100644 --- a/services/registration/pin_reset_mismatch.vis +++ b/services/registration/pin_reset_mismatch.vis @@ -3,4 +3,3 @@ MOUT quit 9 HALT INCMP confirm_pin_change 1 INCMP quit 9 - diff --git a/services/registration/pin_reset_success b/services/registration/pin_reset_success index e9326ec..47a0fcb 100644 --- a/services/registration/pin_reset_success +++ b/services/registration/pin_reset_success @@ -1 +1 @@ -Your PIN change request has been successful +Your PIN change request has been successful \ No newline at end of file diff --git a/services/registration/pin_reset_success.vis b/services/registration/pin_reset_success.vis index c942519..9a5aedb 100644 --- a/services/registration/pin_reset_success.vis +++ b/services/registration/pin_reset_success.vis @@ -1,10 +1,8 @@ LOAD confirm_pin_change 0 RELOAD confirm_pin_change -CATCH pin_reset_mismatch flag_pin_mismatch 1 +CATCH pin_reset_mismatch flag_pin_mismatch 1 MOUT back 0 MOUT quit 9 HALT INCMP main 0 INCMP quit 9 - - diff --git a/services/registration/profile_update_success b/services/registration/profile_update_success index 652942a..d8d5706 100644 --- a/services/registration/profile_update_success +++ b/services/registration/profile_update_success @@ -1 +1 @@ -Profile updated successfully +Profile updated successfully \ No newline at end of file -- 2.45.2 From 35acf75f6b85092f12324e6a899ceff1dd536544 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:31:00 +0300 Subject: [PATCH 02/24] add mock account service --- cmd/main.go | 5 +-- internal/handlers/handlerservice.go | 5 +-- internal/handlers/server/accountservice.go | 41 ++++++++++++++++++++++ internal/handlers/ussd/menuhandler.go | 4 +-- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 9db5e0a..c8055bc 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" ) @@ -84,8 +85,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/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 4cedd26..3b5cbb3 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -7,6 +7,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" + "git.grassecon.net/urdt/ussd/internal/handlers/server" ) type HandlerService interface { @@ -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 } diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index f4375a1..1b144f1 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -18,6 +18,10 @@ type AccountServiceInterface interface { type AccountService struct { } +type MockAccountService struct { +} + + // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. @@ -110,3 +114,40 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return &accountResp, nil } + + + +func (mas *MockAccountService) 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 (mas *MockAccountService) CheckBalance(publicKey string) (string, 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.Result.Balance, nil +} + +func (mas *MockAccountService) CheckAccountStatus(trackingId string) (string, error) { + return "SUCCESS", nil +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8ffecc3..b0427a3 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 } -- 2.45.2 From 5937e7ea9d6a046caa55ab6c5f3864433887e2cd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:31:30 +0300 Subject: [PATCH 03/24] add tag to toggle between online and offline tests --- engine/tag_offline.go | 5 +++++ engine/tag_online.go | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 engine/tag_offline.go create mode 100644 engine/tag_online.go diff --git a/engine/tag_offline.go b/engine/tag_offline.go new file mode 100644 index 0000000..d81433e --- /dev/null +++ b/engine/tag_offline.go @@ -0,0 +1,5 @@ +// +build !online + +package engine + +const OnlineTestEnabled = false \ No newline at end of file diff --git a/engine/tag_online.go b/engine/tag_online.go new file mode 100644 index 0000000..a2ba80b --- /dev/null +++ b/engine/tag_online.go @@ -0,0 +1,5 @@ +// +build online + +package engine + +const OnlineTestEnabled = true \ No newline at end of file -- 2.45.2 From c39c4acc8652b1df507e3331785510b7102ab59e Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:31:47 +0300 Subject: [PATCH 04/24] add test engine --- engine/engine.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 engine/engine.go diff --git a/engine/engine.go b/engine/engine.go new file mode 100644 index 0000000..ea06193 --- /dev/null +++ b/engine/engine.go @@ -0,0 +1,111 @@ +package engine + + +import ( + "context" + "fmt" + "os" + "path" + + "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()) { + var accountService server.AccountServiceInterface + ctx := context.Background() + ctx = context.WithValue(ctx, "SessionId", sessionId) + pfp := path.Join(scriptDir, "pp.csv") + + cfg := engine.Config{ + Root: "root", + SessionId: sessionId, + OutputSize: uint32(160), + FlagCount: uint32(16), + } + + 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 OnlineTestEnabled { + accountService = &server.AccountService{} + } else { + accountService = &server.MockAccountService{} + } + 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") + } + + //en = en.WithDebug(nil) + return en, cleanFn +} + -- 2.45.2 From 2220d286b72ded9f6568b583041f5fab0c6f5d99 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:32:08 +0300 Subject: [PATCH 05/24] add test data files --- test_engine/group_test.json | 368 ++++++++++++++++++++++++++++++++++++ test_engine/test_setup.json | 219 +++++++++++++++++++++ 2 files changed, 587 insertions(+) create mode 100644 test_engine/group_test.json create mode 100644 test_engine/test_setup.json diff --git a/test_engine/group_test.json b/test_engine/group_test.json new file mode 100644 index 0000000..02f2628 --- /dev/null +++ b/test_engine/group_test.json @@ -0,0 +1,368 @@ +{ + "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\n0:Back" + }, + { + "input": "1234", + "expectedContent": "Enter a new four number PIN:\n0:Back" + }, + { + "input": "1234", + "expectedContent": "Confirm your new PIN:\n0:Back" + }, + { + "input": "1234", + "expectedContent": "Your PIN change request has been 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_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_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\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\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\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\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\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\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/test_engine/test_setup.json b/test_engine/test_setup.json new file mode 100644 index 0000000..6793391 --- /dev/null +++ b/test_engine/test_setup.json @@ -0,0 +1,219 @@ +[ + { + "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_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.00 SRFYour account balance is 0.003 CELO" + } + ] + }, + { + "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 balance is: 0.00 SRFYour account balance is 0.003 CELO" + } + ] + }, + { + "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 -- 2.45.2 From 5de82fd08a11a33319d4b59b3b74dbe353eedaa4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:32:28 +0300 Subject: [PATCH 06/24] add menu traversal tests --- test_engine/menu_traversal_test.go | 356 +++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 test_engine/menu_traversal_test.go diff --git a/test_engine/menu_traversal_test.go b/test_engine/menu_traversal_test.go new file mode 100644 index 0000000..e7fe2bf --- /dev/null +++ b/test_engine/menu_traversal_test.go @@ -0,0 +1,356 @@ +package main + +import ( + "bytes" + "context" + "log" + "math/rand" + "os" + "regexp" + "testing" + "time" + + "git.grassecon.net/urdt/ussd/driver" + enginetest "git.grassecon.net/urdt/ussd/engine" + "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 := enginetest.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() + if !bytes.Equal(b, []byte(step.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } + // Adding a sleep after the test to wait for registration to complete the process + time.Sleep(5 * time.Second) +} + +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 := enginetest.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() + if !bytes.Equal(b, []byte(step.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestSendWithInvalidInputs(t *testing.T) { + en, fn := enginetest.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() + + // Extract the dynamic public key from the output + publicKey := extractPublicKey(b) + + // Replace placeholder {public_key} with the actual dynamic public key + expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) + + if !bytes.Equal(b, expectedContent) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b) + } + } + } + } +} + +func TestMyAccount_Check_My_Balance(t *testing.T) { + en, fn := enginetest.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "menu_my_account_check_my_balance") + 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() + if !bytes.Equal(b, []byte(step.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMainMenuHelp(t *testing.T) { + en, fn := enginetest.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() + if !bytes.Equal(b, []byte(step.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMainMenuQuit(t *testing.T) { + en, fn := enginetest.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() + if !bytes.Equal(b, []byte(step.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMyAccount_Check_Community_Balance(t *testing.T) { + en, fn := enginetest.TestEngine(sessionID) + defer fn() + ctx := context.Background() + sessions := testData + for _, session := range sessions { + groups := driver.FilterGroupsByName(session.Groups, "menu_my_account_check_community_balance") + 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() + if !bytes.Equal(b, []byte(step.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) + } + } + } + } +} + +func TestMyAccount_MyAddress(t *testing.T) { + en, fn := enginetest.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) + + if !bytes.Equal(b, expectedContent) { + 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 := enginetest.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() + if !bytes.Equal(b, []byte(tt.ExpectedContent)) { + t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b) + } + + }) + } +} -- 2.45.2 From e1f3ab78f196b5d81da403ab1fb7e7eb05b94c11 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:33:09 +0300 Subject: [PATCH 07/24] 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 -- 2.45.2 From 360b51a3dc01953ce71c49be84ad017b7ec8a8fd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:33:38 +0300 Subject: [PATCH 08/24] 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= -- 2.45.2 From d7bfdc62e96b7273e53599a9381cad77138f8e84 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 7 Oct 2024 23:35:41 +0300 Subject: [PATCH 09/24] setup test data drivers --- driver/groupdriver.go | 60 +++++++++++++++++++++++++++++++++++++++++ driver/setup_driver .go | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 driver/groupdriver.go create mode 100644 driver/setup_driver .go diff --git a/driver/groupdriver.go b/driver/groupdriver.go new file mode 100644 index 0000000..ebb0706 --- /dev/null +++ b/driver/groupdriver.go @@ -0,0 +1,60 @@ +package driver + +import ( + "encoding/json" + "os" +) + +type StepTest struct { + Input string `json:"input"` + ExpectedContent string `json:"expectedContent"` +} + +// Group represents a group of steps. +type GroupTest struct { + Name string `json:"name"` + Steps []Step `json:"steps"` +} + +// DataGroup represents the overall structure of the JSON. +type DataGroup struct { + Groups []GroupTest `json:"groups"` +} + +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) []struct { + Name string + Input string + ExpectedContent string +} { + var tests []struct { + Name string + Input string + ExpectedContent string + } + for _, group := range group.Groups { + for _, step := range group.Steps { + // Create a test case for each group + tests = append(tests, struct { + Name string + Input string + ExpectedContent string + }{ + Name: group.Name, + Input: step.Input, + ExpectedContent: step.ExpectedContent, + }) + } + } + + return tests +} diff --git a/driver/setup_driver .go b/driver/setup_driver .go new file mode 100644 index 0000000..673d417 --- /dev/null +++ b/driver/setup_driver .go @@ -0,0 +1,55 @@ +package driver + +import ( + "encoding/json" + "log" + "os" +) + +type Step struct { + Input string `json:"input"` + ExpectedContent string `json:"expectedContent"` +} + +type Group struct { + Name string `json:"name"` + Steps []Step `json:"steps"` +} + +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 Map[T any, U any](input []T, fn func(T) U) []U { + result := make([]U, len(input)) + for i, v := range input { + result[i] = fn(v) + } + return result +} -- 2.45.2 From d01af482168c93dc0fec2cae0bfd214faa5732f2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 8 Oct 2024 00:03:29 +0300 Subject: [PATCH 10/24] pass account service as a param --- cmd/africastalking/main.go | 4 +++- cmd/async/main.go | 4 +++- cmd/http/main.go | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index c24c4b1..2f92930 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -15,6 +15,7 @@ import ( "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/server" "git.grassecon.net/urdt/ussd/internal/handlers" httpserver "git.grassecon.net/urdt/ussd/internal/http" @@ -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 09236fd..5093409 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -15,6 +15,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/internal/handlers/server" ) var ( @@ -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 6b868ed..2172930 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -18,6 +18,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/internal/handlers/server" ) var ( @@ -88,7 +89,9 @@ 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) -- 2.45.2 From 3afe35b44f28cffbefe1a0f41b70a2c3ba1b4ccb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 16:25:25 +0300 Subject: [PATCH 11/24] remove setup driver --- driver/setup_driver .go | 55 ----------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 driver/setup_driver .go diff --git a/driver/setup_driver .go b/driver/setup_driver .go deleted file mode 100644 index 673d417..0000000 --- a/driver/setup_driver .go +++ /dev/null @@ -1,55 +0,0 @@ -package driver - -import ( - "encoding/json" - "log" - "os" -) - -type Step struct { - Input string `json:"input"` - ExpectedContent string `json:"expectedContent"` -} - -type Group struct { - Name string `json:"name"` - Steps []Step `json:"steps"` -} - -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 Map[T any, U any](input []T, fn func(T) U) []U { - result := make([]U, len(input)) - for i, v := range input { - result[i] = fn(v) - } - return result -} -- 2.45.2 From 810dd74e43936d999d2e6361c6b8a167c67a6317 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 16:44:51 +0300 Subject: [PATCH 12/24] use one test driver --- driver/groupdriver.go | 89 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/driver/groupdriver.go b/driver/groupdriver.go index ebb0706..c6d0a69 100644 --- a/driver/groupdriver.go +++ b/driver/groupdriver.go @@ -2,23 +2,86 @@ package driver import ( "encoding/json" + "log" "os" + "regexp" ) -type StepTest struct { +type Step struct { Input string `json:"input"` ExpectedContent string `json:"expectedContent"` } -// Group represents a group of steps. -type GroupTest struct { +func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { + pattern := `.*\?.*|.*` + 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 +} + +// 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) { + re, err := regexp.Compile(s.ExpectedContent) + 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 []GroupTest `json:"groups"` + 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) { @@ -31,24 +94,12 @@ func LoadTestGroups(filePath string) (DataGroup, error) { return sessionsData, err } -func CreateTestCases(group DataGroup) []struct { - Name string - Input string - ExpectedContent string -} { - var tests []struct { - Name string - Input string - ExpectedContent string - } +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, struct { - Name string - Input string - ExpectedContent string - }{ + tests = append(tests, TestCase{ Name: group.Name, Input: step.Input, ExpectedContent: step.ExpectedContent, -- 2.45.2 From 96aec1fd670efbdf724596b6945a2ba152e83ff4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 16:45:09 +0300 Subject: [PATCH 13/24] define an event channel --- internal/testutil/TestEngine.go | 119 ++++++++++++++++++++++++++++++++ 1 file changed, 119 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..af086ac --- /dev/null +++ b/internal/testutil/TestEngine.go @@ -0,0 +1,119 @@ +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(16), + } + + 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) + } + + switch AccountService.(type) { + case *server.MockAccountService: + 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") + } + + //en = en.WithDebug(nil) + return en, cleanFn, eventChannel +} -- 2.45.2 From e30a7ad3e332c79b03a7816c75455b151f7c473a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 16:45:51 +0300 Subject: [PATCH 14/24] use regex exp to match expected content --- test_engine/menu_traversal_test.go | 82 +++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/test_engine/menu_traversal_test.go b/test_engine/menu_traversal_test.go index e7fe2bf..dc55782 100644 --- a/test_engine/menu_traversal_test.go +++ b/test_engine/menu_traversal_test.go @@ -8,10 +8,9 @@ import ( "os" "regexp" "testing" - "time" "git.grassecon.net/urdt/ussd/driver" - enginetest "git.grassecon.net/urdt/ussd/engine" + "git.grassecon.net/urdt/ussd/internal/testutil" "github.com/gofrs/uuid" ) @@ -55,7 +54,7 @@ func TestMain(m *testing.M) { } func TestAccountCreationSuccessful(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, eventChannel := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -76,14 +75,18 @@ func TestAccountCreationSuccessful(t *testing.T) { t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) } b := w.Bytes() - if !bytes.Equal(b, []byte(step.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) } } } } - // Adding a sleep after the test to wait for registration to complete the process - time.Sleep(5 * time.Second) + <-eventChannel + } func TestAccountRegistrationRejectTerms(t *testing.T) { @@ -94,7 +97,7 @@ func TestAccountRegistrationRejectTerms(t *testing.T) { t.Fail() } edgeCaseSessionID := v.String() - en, fn := enginetest.TestEngine(edgeCaseSessionID) + en, fn, _ := testutil.TestEngine(edgeCaseSessionID) defer fn() ctx := context.Background() sessions := testData @@ -116,7 +119,11 @@ func TestAccountRegistrationRejectTerms(t *testing.T) { } b := w.Bytes() - if !bytes.Equal(b, []byte(step.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) } } @@ -125,7 +132,7 @@ func TestAccountRegistrationRejectTerms(t *testing.T) { } func TestSendWithInvalidInputs(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -153,8 +160,11 @@ func TestSendWithInvalidInputs(t *testing.T) { // Replace placeholder {public_key} with the actual dynamic public key expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) - - if !bytes.Equal(b, 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) } } @@ -163,7 +173,7 @@ func TestSendWithInvalidInputs(t *testing.T) { } func TestMyAccount_Check_My_Balance(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -185,7 +195,11 @@ func TestMyAccount_Check_My_Balance(t *testing.T) { t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) } b := w.Bytes() - if !bytes.Equal(b, []byte(step.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) } } @@ -194,7 +208,7 @@ func TestMyAccount_Check_My_Balance(t *testing.T) { } func TestMainMenuHelp(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -216,7 +230,11 @@ func TestMainMenuHelp(t *testing.T) { } b := w.Bytes() - if !bytes.Equal(b, []byte(step.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) } } @@ -225,7 +243,7 @@ func TestMainMenuHelp(t *testing.T) { } func TestMainMenuQuit(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -247,7 +265,11 @@ func TestMainMenuQuit(t *testing.T) { } b := w.Bytes() - if !bytes.Equal(b, []byte(step.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) } } @@ -256,7 +278,7 @@ func TestMainMenuQuit(t *testing.T) { } func TestMyAccount_Check_Community_Balance(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -278,7 +300,11 @@ func TestMyAccount_Check_Community_Balance(t *testing.T) { t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) } b := w.Bytes() - if !bytes.Equal(b, []byte(step.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) } } @@ -287,7 +313,7 @@ func TestMyAccount_Check_Community_Balance(t *testing.T) { } func TestMyAccount_MyAddress(t *testing.T) { - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() sessions := testData @@ -311,10 +337,12 @@ func TestMyAccount_MyAddress(t *testing.T) { b := w.Bytes() publicKey := extractPublicKey(b) - expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) - - if !bytes.Equal(b, 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) } } @@ -327,7 +355,7 @@ func TestGroups(t *testing.T) { if err != nil { log.Fatalf("Failed to load test groups: %v", err) } - en, fn := enginetest.TestEngine(sessionID) + en, fn, _ := testutil.TestEngine(sessionID) defer fn() ctx := context.Background() // Create test cases from loaded groups @@ -347,7 +375,11 @@ func TestGroups(t *testing.T) { t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err) } b := w.Bytes() - if !bytes.Equal(b, []byte(tt.ExpectedContent)) { + 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) } -- 2.45.2 From c6227acba169f247bfa60c9d2493ff0a7d439f08 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 16:46:22 +0300 Subject: [PATCH 15/24] use build tag to define account service --- internal/testutil/tag_offline.go | 13 +++++++++++++ internal/testutil/tag_online.go | 11 +++++++++++ 2 files changed, 24 insertions(+) create mode 100644 internal/testutil/tag_offline.go create mode 100644 internal/testutil/tag_online.go diff --git a/internal/testutil/tag_offline.go b/internal/testutil/tag_offline.go new file mode 100644 index 0000000..fc57c90 --- /dev/null +++ b/internal/testutil/tag_offline.go @@ -0,0 +1,13 @@ +// +build !online + +package testutil + +import ( + "git.grassecon.net/urdt/ussd/internal/handlers/server" +) + +var AccountService server.AccountServiceInterface + +func init() { + AccountService = &server.MockAccountService{} +} \ No newline at end of file diff --git a/internal/testutil/tag_online.go b/internal/testutil/tag_online.go new file mode 100644 index 0000000..cc3f00c --- /dev/null +++ b/internal/testutil/tag_online.go @@ -0,0 +1,11 @@ +// +build online + +package testutil + +import "git.grassecon.net/urdt/ussd/internal/handlers/server" + +var AccountService server.AccountServiceInterface + +func init() { + AccountService = &server.AccountService{} +} -- 2.45.2 From c8a5391435fe7bee9839072d30fa60a9d1754dd4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 16:47:21 +0300 Subject: [PATCH 16/24] remove extra line --- services/registration/edit_profile.vis | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index a39fff4..e1712c4 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -18,5 +18,4 @@ INCMP select_gender 3 INCMP enter_yob 4 INCMP enter_location 5 INCMP enter_offerings 6 -INCMP view_profile 7 - +INCMP view_profile 7 \ No newline at end of file -- 2.45.2 From 8791753b2a1e8c9699c16d963d2f0195ecdb8ce2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 22:21:11 +0300 Subject: [PATCH 17/24] ignore special character in regex --- driver/groupdriver.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/driver/groupdriver.go b/driver/groupdriver.go index c6d0a69..9b3796e 100644 --- a/driver/groupdriver.go +++ b/driver/groupdriver.go @@ -13,13 +13,9 @@ type Step struct { } func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { - pattern := `.*\?.*|.*` - re, err := regexp.Compile(pattern) - if err != nil { - return false, err - } - // Check if the content matches the regex pattern - if re.Match(content) { + pattern := regexp.QuoteMeta(s.ExpectedContent) + re, _ := regexp.Compile(pattern) + if re.Match([]byte(content)) { return true, nil } return false, nil @@ -38,7 +34,8 @@ type TestCase struct { } func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) { - re, err := regexp.Compile(s.ExpectedContent) + pattern := regexp.QuoteMeta(s.ExpectedContent) + re, err := regexp.Compile(pattern) if err != nil { return false, err } -- 2.45.2 From 85efd2ac6e99e720dc9858cc86a40feae62b0c5a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 9 Oct 2024 22:22:23 +0300 Subject: [PATCH 18/24] match step expected content after replacing actual data --- test_engine/menu_traversal_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_engine/menu_traversal_test.go b/test_engine/menu_traversal_test.go index dc55782..98f36a2 100644 --- a/test_engine/menu_traversal_test.go +++ b/test_engine/menu_traversal_test.go @@ -160,6 +160,7 @@ func TestSendWithInvalidInputs(t *testing.T) { // Replace placeholder {public_key} with the actual dynamic public key 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) @@ -338,6 +339,7 @@ func TestMyAccount_MyAddress(t *testing.T) { 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) -- 2.45.2 From 4d5735f85f5fd4fef33495f04a59ff2f8ff32830 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 10 Oct 2024 09:13:04 +0300 Subject: [PATCH 19/24] add new line --- services/registration/edit_profile.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index e1712c4..277f330 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -18,4 +18,4 @@ INCMP select_gender 3 INCMP enter_yob 4 INCMP enter_location 5 INCMP enter_offerings 6 -INCMP view_profile 7 \ No newline at end of file +INCMP view_profile 7 -- 2.45.2 From 0837933b6c83cf7d5c73d72364a407a6bed682e1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 10 Oct 2024 16:53:59 +0300 Subject: [PATCH 20/24] move engine to testutil --- engine/engine.go | 111 ------------------------------------------ engine/tag_offline.go | 5 -- engine/tag_online.go | 5 -- 3 files changed, 121 deletions(-) delete mode 100644 engine/engine.go delete mode 100644 engine/tag_offline.go delete mode 100644 engine/tag_online.go diff --git a/engine/engine.go b/engine/engine.go deleted file mode 100644 index ea06193..0000000 --- a/engine/engine.go +++ /dev/null @@ -1,111 +0,0 @@ -package engine - - -import ( - "context" - "fmt" - "os" - "path" - - "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()) { - var accountService server.AccountServiceInterface - ctx := context.Background() - ctx = context.WithValue(ctx, "SessionId", sessionId) - pfp := path.Join(scriptDir, "pp.csv") - - cfg := engine.Config{ - Root: "root", - SessionId: sessionId, - OutputSize: uint32(160), - FlagCount: uint32(16), - } - - 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 OnlineTestEnabled { - accountService = &server.AccountService{} - } else { - accountService = &server.MockAccountService{} - } - 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") - } - - //en = en.WithDebug(nil) - return en, cleanFn -} - diff --git a/engine/tag_offline.go b/engine/tag_offline.go deleted file mode 100644 index d81433e..0000000 --- a/engine/tag_offline.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build !online - -package engine - -const OnlineTestEnabled = false \ No newline at end of file diff --git a/engine/tag_online.go b/engine/tag_online.go deleted file mode 100644 index a2ba80b..0000000 --- a/engine/tag_online.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build online - -package engine - -const OnlineTestEnabled = true \ No newline at end of file -- 2.45.2 From d9e922d5be40a8bede0e355b78fb56bb27fe4ec1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 12 Oct 2024 13:30:46 +0300 Subject: [PATCH 21/24] return error when regex generation fails --- driver/groupdriver.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/driver/groupdriver.go b/driver/groupdriver.go index 9b3796e..68cb7e3 100644 --- a/driver/groupdriver.go +++ b/driver/groupdriver.go @@ -14,7 +14,10 @@ type Step struct { func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { pattern := regexp.QuoteMeta(s.ExpectedContent) - re, _ := regexp.Compile(pattern) + re, err := regexp.Compile(pattern) + if err != nil { + return false, err + } if re.Match([]byte(content)) { return true, nil } -- 2.45.2 From 554cecc7f38d59500364dcdd98b91f723efe4168 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 12 Oct 2024 13:31:26 +0300 Subject: [PATCH 22/24] chore: rename entities --- internal/handlers/server/accountservice.go | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/internal/handlers/server/accountservice.go b/internal/handlers/server/accountservice.go index 1b144f1..469a388 100644 --- a/internal/handlers/server/accountservice.go +++ b/internal/handlers/server/accountservice.go @@ -18,12 +18,9 @@ type AccountServiceInterface interface { type AccountService struct { } -type MockAccountService struct { +type TestAccountService struct { } - - - // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID. // // Parameters: @@ -31,12 +28,10 @@ type MockAccountService 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 { @@ -54,13 +49,10 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) if err != nil { return "", err } - status := trackResp.Result.Transaction.Status - 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. @@ -87,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. @@ -115,9 +106,7 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { return &accountResp, nil } - - -func (mas *MockAccountService) CreateAccount() (*models.AccountResponse, error) { +func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { return &models.AccountResponse{ Ok: true, Result: struct { @@ -132,7 +121,7 @@ func (mas *MockAccountService) CreateAccount() (*models.AccountResponse, error) }, nil } -func (mas *MockAccountService) CheckBalance(publicKey string) (string, error) { +func (tas *TestAccountService) CheckBalance(publicKey string) (string, error) { balanceResponse := &models.BalanceResponse{ Ok: true, @@ -148,6 +137,6 @@ func (mas *MockAccountService) CheckBalance(publicKey string) (string, error) { return balanceResponse.Result.Balance, nil } -func (mas *MockAccountService) CheckAccountStatus(trackingId string) (string, error) { +func (tas *TestAccountService) CheckAccountStatus(trackingId string) (string, error) { return "SUCCESS", nil } -- 2.45.2 From 70e234aa7c5d3f4d86d8496e7a0198cf0d7af63b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 12 Oct 2024 13:32:34 +0300 Subject: [PATCH 23/24] pass account service as a global var --- internal/testutil/tag_offline.go | 9 ++++----- internal/testutil/tag_online.go | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/internal/testutil/tag_offline.go b/internal/testutil/tag_offline.go index fc57c90..2335a5e 100644 --- a/internal/testutil/tag_offline.go +++ b/internal/testutil/tag_offline.go @@ -1,3 +1,4 @@ +//go:build !online // +build !online package testutil @@ -6,8 +7,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers/server" ) -var AccountService server.AccountServiceInterface - -func init() { - AccountService = &server.MockAccountService{} -} \ No newline at end of file +var ( + AccountService server.AccountServiceInterface = &server.TestAccountService{} +) diff --git a/internal/testutil/tag_online.go b/internal/testutil/tag_online.go index cc3f00c..5a7e928 100644 --- a/internal/testutil/tag_online.go +++ b/internal/testutil/tag_online.go @@ -1,11 +1,10 @@ +//go:build online // +build online package testutil import "git.grassecon.net/urdt/ussd/internal/handlers/server" -var AccountService server.AccountServiceInterface - -func init() { - AccountService = &server.AccountService{} -} +var ( + AccountService server.AccountServiceInterface +) -- 2.45.2 From c21a48c78ee0541b9f88db0701c9f229d4bb05a2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 12 Oct 2024 13:33:13 +0300 Subject: [PATCH 24/24] update test engine --- internal/testutil/TestEngine.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index af086ac..8daf387 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -79,8 +79,12 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { os.Exit(1) } + if AccountService == nil { + AccountService = &server.AccountService{} + } + switch AccountService.(type) { - case *server.MockAccountService: + case *server.TestAccountService: go func() { eventChannel <- false }() -- 2.45.2