diff --git a/internal/ssh/keystore.go b/internal/ssh/keystore.go deleted file mode 100644 index 68bfe5d..0000000 --- a/internal/ssh/keystore.go +++ /dev/null @@ -1,65 +0,0 @@ -package ssh - -import ( - "context" - "fmt" - "os" - "path" - - "golang.org/x/crypto/ssh" - - "git.defalsify.org/vise.git/db" - - "git.grassecon.net/grassrootseconomics/visedriver/storage" - dbstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db/gdbm" -) - -type SshKeyStore struct { - store db.Db -} - -func NewSshKeyStore(ctx context.Context, dbDir string) (*SshKeyStore, error) { - keyStore := &SshKeyStore{} - keyStoreFile := path.Join(dbDir, "ssh_authorized_keys.gdbm") - keyStore.store = dbstorage.NewThreadGdbmDb() - err := keyStore.store.Connect(ctx, keyStoreFile) - if err != nil { - return nil, err - } - return keyStore, nil -} - -func(s *SshKeyStore) AddFromFile(ctx context.Context, fp string, sessionId string) error { - _, err := os.Stat(fp) - if err != nil { - return fmt.Errorf("cannot open ssh server public key file: %v\n", err) - } - - publicBytes, err := os.ReadFile(fp) - if err != nil { - return fmt.Errorf("Failed to load public key: %v", err) - } - pubKey, _, _, _, err := ssh.ParseAuthorizedKey(publicBytes) - if err != nil { - return fmt.Errorf("Failed to parse public key: %v", err) - } - k := append([]byte{0x01}, pubKey.Marshal()...) - s.store.SetPrefix(storage.DATATYPE_EXTEND) - logg.Infof("Added key", "sessionId", sessionId, "public key", string(publicBytes)) - return s.store.Put(ctx, k, []byte(sessionId)) -} - -func(s *SshKeyStore) Get(ctx context.Context, pubKey ssh.PublicKey) (string, error) { - s.store.SetLanguage(nil) - s.store.SetPrefix(storage.DATATYPE_EXTEND) - k := append([]byte{0x01}, pubKey.Marshal()...) - v, err := s.store.Get(ctx, k) - if err != nil { - return "", err - } - return string(v), nil -} - -func(s *SshKeyStore) Close() error { - return s.store.Close() -} diff --git a/internal/ssh/ssh.go b/internal/ssh/ssh.go deleted file mode 100644 index 426bac9..0000000 --- a/internal/ssh/ssh.go +++ /dev/null @@ -1,284 +0,0 @@ -package ssh - -import ( - "context" - "encoding/hex" - "encoding/base64" - "errors" - "fmt" - "net" - "os" - "sync" - - "golang.org/x/crypto/ssh" - - "git.defalsify.org/vise.git/engine" - "git.defalsify.org/vise.git/logging" - "git.defalsify.org/vise.git/resource" - "git.defalsify.org/vise.git/state" - - "git.grassecon.net/grassrootseconomics/visedriver/handlers" - "git.grassecon.net/grassrootseconomics/visedriver/storage" - "git.grassecon.net/grassrootseconomics/visedriver/remote" -) - -var ( - logg = logging.NewVanilla().WithDomain("ssh") -) - -type auther struct { - Ctx context.Context - keyStore *SshKeyStore - auth map[string]string -} - -func NewAuther(ctx context.Context, keyStore *SshKeyStore) *auther { - return &auther{ - Ctx: ctx, - keyStore: keyStore, - auth: make(map[string]string), - } -} - -func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { - logg.TraceCtxf(a.Ctx, "looking for publickey", "pubkey", fmt.Sprintf("%x", pubKey)) - va, err := a.keyStore.Get(a.Ctx, pubKey) - if err != nil { - return nil, err - } - ka := hex.EncodeToString(conn.SessionID()) - a.auth[ka] = va - fmt.Fprintf(os.Stderr, "connect: %s -> %s\n", ka, va) - return nil, nil -} - -func(a *auther) FromConn(c *ssh.ServerConn) (string, error) { - if c == nil { - return "", errors.New("nil server conn") - } - if c.Conn == nil { - return "", errors.New("nil underlying conn") - } - return a.Get(c.Conn.SessionID()) -} - - -func(a *auther) Get(k []byte) (string, error) { - ka := hex.EncodeToString(k) - v, ok := a.auth[ka] - if !ok { - return "", errors.New("not found") - } - return v, nil -} - -type SshRunner struct { - Ctx context.Context - Cfg engine.Config - FlagFile string - Conn storage.ConnData - ResourceDir string - Debug bool - SrvKeyFile string - Host string - Port uint - wg sync.WaitGroup - lst net.Listener -} - -func(s *SshRunner) serve(ctx context.Context, sessionId string, ch ssh.NewChannel, en engine.Engine) error { - if ch == nil { - return errors.New("nil channel") - } - if ch.ChannelType() != "session" { - ch.Reject(ssh.UnknownChannelType, "that is not the channel you are looking for") - return errors.New("not a session") - } - channel, requests, err := ch.Accept() - if err != nil { - panic(err) - } - defer channel.Close() - s.wg.Add(1) - go func(reqIn <-chan *ssh.Request) { - defer s.wg.Done() - for req := range reqIn { - req.Reply(req.Type == "shell", nil) - } - _ = requests - }(requests) - - cont, err := en.Exec(ctx, []byte{}) - if err != nil { - return fmt.Errorf("initial engine exec err: %v", err) - } - - var input [state.INPUT_LIMIT]byte - for cont { - c, err := en.Flush(ctx, channel) - if err != nil { - return fmt.Errorf("flush err: %v", err) - } - _, err = channel.Write([]byte{0x0a}) - if err != nil { - return fmt.Errorf("newline err: %v", err) - } - c, err = channel.Read(input[:]) - if err != nil { - return fmt.Errorf("read input fail: %v", err) - } - logg.TraceCtxf(ctx, "input read", "c", c, "input", input[:c-1]) - cont, err = en.Exec(ctx, input[:c-1]) - if err != nil { - return fmt.Errorf("engine exec err: %v", err) - } - logg.TraceCtxf(ctx, "exec cont", "cont", cont, "en", en) - _ = c - } - c, err := en.Flush(ctx, channel) - if err != nil { - return fmt.Errorf("last flush err: %v", err) - } - _ = c - return nil -} - -func(s *SshRunner) Stop() error { - return s.lst.Close() -} - -func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) { - ctx := s.Ctx - menuStorageService := storage.NewMenuStorageService(s.Conn, s.ResourceDir) - - rs, err := menuStorageService.GetResource(ctx) - if err != nil { - return nil, nil, err - } - - pe, err := menuStorageService.GetPersister(ctx) - if err != nil { - return nil, nil, err - } - - userdatastore, err := menuStorageService.GetUserdataDb(ctx) - if err != nil { - return nil, nil, err - } - - dbResource, ok := rs.(*resource.DbResource) - if !ok { - return nil, nil, err - } - - lhs, err := handlers.NewLocalHandlerService(ctx, s.FlagFile, true, dbResource, s.Cfg, rs) - lhs.SetDataStore(&userdatastore) - lhs.SetPersister(pe) - lhs.Cfg.SessionId = sessionId - - if err != nil { - return nil, nil, err - } - - // TODO: clear up why pointer here and by-value other cmds - accountService := &remote.AccountService{} - hl, err := lhs.GetHandler(accountService) - if err != nil { - return nil, nil, err - } - - en := lhs.GetEngine() - en = en.WithFirst(hl.Init) - if s.Debug { - en = en.WithDebug(nil) - } - // TODO: this is getting very hacky! - closer := func() { - err := menuStorageService.Close() - if err != nil { - logg.ErrorCtxf(ctx, "menu storage service cleanup fail", "err", err) - } - } - return en, closer, nil -} - -// adapted example from crypto/ssh package, NewServerConn doc -func(s *SshRunner) Run(ctx context.Context, keyStore *SshKeyStore) { - s.Ctx = ctx - running := true - - // TODO: waitgroup should probably not be global - defer s.wg.Wait() - - auth := NewAuther(ctx, keyStore) - cfg := ssh.ServerConfig{ - PublicKeyCallback: auth.Check, - } - - privateBytes, err := os.ReadFile(s.SrvKeyFile) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to load private key", "err", err) - } - private, err := ssh.ParsePrivateKey(privateBytes) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to parse private key", "err", err) - } - srvPub := private.PublicKey() - srvPubStr := base64.StdEncoding.EncodeToString(srvPub.Marshal()) - logg.InfoCtxf(ctx, "have server key", "type", srvPub.Type(), "public", srvPubStr) - cfg.AddHostKey(private) - - s.lst, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port)) - if err != nil { - panic(err) - } - - for running { - conn, err := s.lst.Accept() - if err != nil { - logg.ErrorCtxf(ctx, "ssh accept error", "err", err) - running = false - continue - } - - go func(conn net.Conn) { - defer conn.Close() - for true { - srvConn, nC, rC, err := ssh.NewServerConn(conn, &cfg) - if err != nil { - logg.InfoCtxf(ctx, "rejected client", "err", err) - return - } - logg.DebugCtxf(ctx, "ssh client connected", "conn", srvConn) - - s.wg.Add(1) - go func() { - ssh.DiscardRequests(rC) - s.wg.Done() - }() - - sessionId, err := auth.FromConn(srvConn) - if err != nil { - logg.ErrorCtxf(ctx, "Cannot find authentication") - return - } - en, closer, err := s.GetEngine(sessionId) - if err != nil { - logg.ErrorCtxf(ctx, "engine won't start", "err", err) - return - } - defer func() { - err := en.Finish() - if err != nil { - logg.ErrorCtxf(ctx, "engine won't stop", "err", err) - } - closer() - }() - for ch := range nC { - err = s.serve(ctx, sessionId, ch, en) - logg.ErrorCtxf(ctx, "ssh server finish", "err", err) - } - } - }(conn) - } -} diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json deleted file mode 100644 index 0ffb49f..0000000 --- a/menutraversal_test/group_test.json +++ /dev/null @@ -1,443 +0,0 @@ -{ - "groups": [ - { - "name": "my_account_change_pin", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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\n0:Back" - }, - { - "input": "1", - "expectedContent": "Enter your old PIN\n\n0:Back" - }, - { - "input": "1234", - "expectedContent": "Enter a new four number PIN:\n\n0:Back" - }, - { - "input": "1234", - "expectedContent": "Confirm your new PIN:\n\n0:Back" - }, - { - "input": "1234", - "expectedContent": "Your PIN change request has been successful\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_language_change", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Select language:\n1:English\n2:Kiswahili" - }, - { - "input": "1", - "expectedContent": "Your language change request was successful.\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_check_my_balance", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Balance: {balance}\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" - }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_check_community_balance", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "{balance}\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" - }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_edit_all_account_details_starting_from_firstname", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "Enter family name:\n0:Back" - }, - { - "input": "bar", - "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" - }, - { - "input": "1", - "expectedContent": "Enter your year of birth\n0:Back" - }, - { - "input": "1940", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_edit_familyname_when_all_account__details_have_been_set", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "1", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "2", - "expectedContent": "Enter family name:\n0:Back" - }, - { - "input": "bar", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_edit_gender_when_all_account__details_have_been_set", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "1", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "3", - "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" - }, - { - "input": "1", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_edit_yob_when_all_account__details_have_been_set", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "1", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "4", - "expectedContent": "Enter your year of birth\n0:Back" - }, - { - "input": "1945", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_edit_location_when_all_account_details_have_been_set", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "1", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "5", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_edit_offerings_when_all_account__details_have_been_set", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "1", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "6", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - }, - { - "name": "menu_my_account_view_profile", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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: 80\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} \ No newline at end of file diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go deleted file mode 100644 index edc5c0c..0000000 --- a/menutraversal_test/menu_traversal_test.go +++ /dev/null @@ -1,386 +0,0 @@ -package menutraversaltest - -import ( - "bytes" - "context" - "flag" - "log" - "math/rand" - "regexp" - "testing" - - "git.grassecon.net/grassrootseconomics/visedriver/testutil" - "git.grassecon.net/grassrootseconomics/visedriver/testutil/driver" - "github.com/gofrs/uuid" -) - -var ( - testData = driver.ReadData() - sessionID string - src = rand.NewSource(42) - g = rand.New(src) -) - -var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests") -var database = flag.String("db", "gdbm", "Specify the database (gdbm or postgres)") -var connStr = flag.String("conn", ".test_state", "connection string") -var dbSchema = flag.String("schema", "test", "Specify the database schema (default test)") - -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 "" -} - -// Extracts the balance value from the engine response. -func extractBalance(response []byte) string { - // Regex to match "Balance: " followed by a newline - re := regexp.MustCompile(`(?m)^Balance:\s+(\d+(\.\d+)?)\s+([A-Z]+)`) - match := re.FindSubmatch(response) - if match != nil { - return string(match[1]) + " " + string(match[3]) // " " - } - return "" -} - -// Extracts the Maximum amount value from the engine response. -func extractMaxAmount(response []byte) string { - // Regex to match "Maximum amount: " followed by a newline - re := regexp.MustCompile(`(?m)^Maximum amount:\s+(\d+(\.\d+)?)`) - match := re.FindSubmatch(response) - if match != nil { - return string(match[1]) // "" - } - return "" -} - -// Extracts the send amount value from the engine response. -func extractSendAmount(response []byte) string { - // Regex to match the pattern "will receive X.XX SYM from" - re := regexp.MustCompile(`will receive (\d+\.\d{2}\s+[A-Z]+) from`) - match := re.FindSubmatch(response) - if match != nil { - return string(match[1]) // Returns "X.XX SYM" - } - return "" -} - -func TestMain(m *testing.M) { - // Parse the flags - flag.Parse() - sessionID = GenerateSessionId() - // set the db - testutil.SetDatabase(*database, *connStr, *dbSchema) - - // Cleanup the db after tests - defer testutil.CleanDatabase() - - m.Run() -} - -func TestAccountCreationSuccessful(t *testing.T) { - en, fn, eventChannel := testutil.TestEngine(sessionID) - defer fn() - ctx := context.Background() - sessions := testData - for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "account_creation_successful") - for _, group := range groups { - for _, step := range group.Steps { - cont, err := en.Exec(ctx, []byte(step.Input)) - if err != nil { - t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) - } - if !cont { - break - } - w := bytes.NewBuffer(nil) - _, err = en.Flush(ctx, w) - if err != nil { - t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) - } - b := w.Bytes() - match, err := step.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) - } - } - } - } - <-eventChannel -} - -func TestAccountRegistrationRejectTerms(t *testing.T) { - // Generate a new UUID for this edge case test - uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g)) - v, err := uu.NewV4() - if err != nil { - t.Fail() - } - edgeCaseSessionID := v.String() - en, fn, _ := testutil.TestEngine(edgeCaseSessionID) - defer fn() - ctx := context.Background() - sessions := testData - for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "account_creation_reject_terms") - for _, group := range groups { - for _, step := range group.Steps { - cont, err := en.Exec(ctx, []byte(step.Input)) - if err != nil { - t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) - return - } - if !cont { - break - } - w := bytes.NewBuffer(nil) - if _, err := en.Flush(ctx, w); err != nil { - t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) - } - - b := w.Bytes() - match, err := step.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) - } - } - } - } -} - -func TestMainMenuHelp(t *testing.T) { - en, fn, _ := testutil.TestEngine(sessionID) - defer fn() - ctx := context.Background() - sessions := testData - for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "main_menu_help") - for _, group := range groups { - for _, step := range group.Steps { - cont, err := en.Exec(ctx, []byte(step.Input)) - if err != nil { - t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) - return - } - if !cont { - break - } - w := bytes.NewBuffer(nil) - if _, err := en.Flush(ctx, w); err != nil { - t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) - } - - b := w.Bytes() - balance := extractBalance(b) - - expectedContent := []byte(step.ExpectedContent) - expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) - - step.ExpectedContent = string(expectedContent) - match, err := step.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) - } - } - } - } -} - -func TestMainMenuQuit(t *testing.T) { - en, fn, _ := testutil.TestEngine(sessionID) - defer fn() - ctx := context.Background() - sessions := testData - for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "main_menu_quit") - for _, group := range groups { - for _, step := range group.Steps { - cont, err := en.Exec(ctx, []byte(step.Input)) - if err != nil { - t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) - return - } - if !cont { - break - } - w := bytes.NewBuffer(nil) - if _, err := en.Flush(ctx, w); err != nil { - t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) - } - - b := w.Bytes() - balance := extractBalance(b) - - expectedContent := []byte(step.ExpectedContent) - expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) - - step.ExpectedContent = string(expectedContent) - match, err := step.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) - } - } - } - } -} - -func TestMyAccount_MyAddress(t *testing.T) { - en, fn, _ := testutil.TestEngine(sessionID) - defer fn() - ctx := context.Background() - sessions := testData - for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "menu_my_account_my_address") - for _, group := range groups { - for index, step := range group.Steps { - t.Logf("step %v with input %v", index, step.Input) - cont, err := en.Exec(ctx, []byte(step.Input)) - if err != nil { - t.Errorf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) - return - } - if !cont { - break - } - w := bytes.NewBuffer(nil) - if _, err := en.Flush(ctx, w); err != nil { - t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) - } - b := w.Bytes() - - balance := extractBalance(b) - publicKey := extractPublicKey(b) - - expectedContent := []byte(step.ExpectedContent) - expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) - expectedContent = bytes.Replace(expectedContent, []byte("{public_key}"), []byte(publicKey), -1) - - step.ExpectedContent = string(expectedContent) - match, err := step.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b) - } - } - } - } -} - -func TestMainMenuSend(t *testing.T) { - en, fn, _ := testutil.TestEngine(sessionID) - defer fn() - ctx := context.Background() - sessions := testData - for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "send_with_invite") - 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.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) - return - } - if !cont { - break - } - w := bytes.NewBuffer(nil) - if _, err := en.Flush(ctx, w); err != nil { - t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) - } - - b := w.Bytes() - balance := extractBalance(b) - max_amount := extractMaxAmount(b) - send_amount := extractSendAmount(b) - - expectedContent := []byte(step.ExpectedContent) - expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) - expectedContent = bytes.Replace(expectedContent, []byte("{max_amount}"), []byte(max_amount), -1) - expectedContent = bytes.Replace(expectedContent, []byte("{send_amount}"), []byte(send_amount), -1) - expectedContent = bytes.Replace(expectedContent, []byte("{session_id}"), []byte(sessionID), -1) - - step.ExpectedContent = string(expectedContent) - match, err := step.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) - } - } - } - } -} - -func TestGroups(t *testing.T) { - groups, err := driver.LoadTestGroups(*groupTestFile) - if err != nil { - log.Fatalf("Failed to load test groups: %v", err) - } - en, fn, _ := testutil.TestEngine(sessionID) - defer fn() - ctx := context.Background() - // Create test cases from loaded groups - tests := driver.CreateTestCases(groups) - for _, tt := range tests { - t.Run(tt.Name, func(t *testing.T) { - cont, err := en.Exec(ctx, []byte(tt.Input)) - if err != nil { - t.Errorf("Test case '%s' failed at input '%s': %v", tt.Name, tt.Input, err) - return - } - if !cont { - return - } - w := bytes.NewBuffer(nil) - if _, err := en.Flush(ctx, w); err != nil { - t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err) - } - b := w.Bytes() - balance := extractBalance(b) - - expectedContent := []byte(tt.ExpectedContent) - expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1) - - tt.ExpectedContent = string(expectedContent) - - match, err := tt.MatchesExpectedContent(b) - if err != nil { - t.Fatalf("Error compiling regex for step '%s': %v", tt.Input, err) - } - if !match { - t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b) - } - }) - } -} diff --git a/menutraversal_test/profile_edit_start_familyname.json b/menutraversal_test/profile_edit_start_familyname.json deleted file mode 100644 index 98325b0..0000000 --- a/menutraversal_test/profile_edit_start_familyname.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_all_account_details_starting_from_family_name", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" - }, - { - "input": "1", - "expectedContent": "Enter your year of birth\n0:Back" - }, - { - "input": "1940", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} - - - - - - - - - - - \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_firstname.json b/menutraversal_test/profile_edit_start_firstname.json deleted file mode 100644 index 0f6be8b..0000000 --- a/menutraversal_test/profile_edit_start_firstname.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_all_account_details_starting_from_firstname", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "Enter family name:\n0:Back" - }, - { - "input": "bar", - "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" - }, - { - "input": "1", - "expectedContent": "Enter your year of birth\n0:Back" - }, - { - "input": "1940", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} diff --git a/menutraversal_test/profile_edit_start_gender.json b/menutraversal_test/profile_edit_start_gender.json deleted file mode 100644 index afca12a..0000000 --- a/menutraversal_test/profile_edit_start_gender.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_all_account_details_starting_from_gender", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "Enter your year of birth\n0:Back" - }, - { - "input": "1940", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} - - \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_location.json b/menutraversal_test/profile_edit_start_location.json deleted file mode 100644 index 8852911..0000000 --- a/menutraversal_test/profile_edit_start_location.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_all_account_details_starting_from_location", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} - \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_offerings.json b/menutraversal_test/profile_edit_start_offerings.json deleted file mode 100644 index 6aa40f6..0000000 --- a/menutraversal_test/profile_edit_start_offerings.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_all_account_details_starting_from_offerings", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, - { - "input": "1", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "6", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} - \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_yob.json b/menutraversal_test/profile_edit_start_yob.json deleted file mode 100644 index 45227f7..0000000 --- a/menutraversal_test/profile_edit_start_yob.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_all_account_details_starting_from_yob", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "1940", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} - \ No newline at end of file diff --git a/menutraversal_test/profile_edit_when_adjacent_item_set.json b/menutraversal_test/profile_edit_when_adjacent_item_set.json deleted file mode 100644 index f8d7263..0000000 --- a/menutraversal_test/profile_edit_when_adjacent_item_set.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "groups": [ - { - "name": "menu_my_account_edit_familyname_when_adjacent_profile_information_set", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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": "Enter your year of birth\n0:Back" - }, - { - "input": "1940", - "expectedContent": "Enter your location:\n0:Back" - }, - { - "input": "Kilifi", - "expectedContent": "Enter the services or goods you offer: \n0:Back" - }, - { - "input": "Bananas", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "2", - "expectedContent": "Enter family name:\n0:Back" - }, - { - "input": "foo2", - "expectedContent": "Please enter your PIN:" - }, - { - "input": "1234", - "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" - }, - { - "input": "0", - "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" - }, - { - "input": "0", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - } - ] - } - ] -} - \ No newline at end of file diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json deleted file mode 100644 index 8728640..0000000 --- a/menutraversal_test/test_setup.json +++ /dev/null @@ -1,133 +0,0 @@ -[ - { - "name": "session one", - "groups": [ - { - "name": "account_creation_successful", - "steps": [ - { - "input": "", - "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n1:English\n2:Kiswahili" - }, - { - "input": "1", - "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n1:Yes\n2:No" - }, - { - "input": "1", - "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\n1:English\n2:Kiswahili" - }, - { - "input": "1", - "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n1:Yes\n2:No" - }, - { - "input": "2", - "expectedContent": "Thank you for using Sarafu. Goodbye!" - } - ] - }, - { - "name": "send_with_invite", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "1", - "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" - }, - { - "input": "0@0", - "expectedContent": "0@0 is invalid, please try again:\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" - }, - { - "input": "0712345678", - "expectedContent": "0712345678 is not registered, please try again:\n1:Retry\n2:Invite to Sarafu Network\n9:Quit" - }, - { - "input": "2", - "expectedContent": "Your invite request for 0712345678 to Sarafu Network failed. Please try again later." - } - ] - }, - { - "name": "main_menu_help", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" - }, - { - "input": "9", - "expectedContent": "Thank you for using Sarafu. Goodbye!" - } - ] - }, - { - "name": "menu_my_account_my_address", - "steps": [ - { - "input": "", - "expectedContent": "{balance}\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}\n0:Back\n9:Quit" - }, - { - "input": "9", - "expectedContent": "Thank you for using Sarafu. Goodbye!" - } - ] - } - ] - } -] \ No newline at end of file diff --git a/testutil/engine.go b/testutil/engine.go deleted file mode 100644 index 7d68f8f..0000000 --- a/testutil/engine.go +++ /dev/null @@ -1,209 +0,0 @@ -package testutil - -import ( - "context" - "fmt" - "log" - "net/url" - "os" - "path" - "path/filepath" - "time" - - "github.com/jackc/pgx/v5/pgxpool" - "git.defalsify.org/vise.git/engine" - "git.defalsify.org/vise.git/logging" - "git.defalsify.org/vise.git/resource" - "git.grassecon.net/grassrootseconomics/visedriver/initializers" - "git.grassecon.net/grassrootseconomics/visedriver/config" - "git.grassecon.net/grassrootseconomics/visedriver/handlers" - "git.grassecon.net/grassrootseconomics/visedriver/storage" - "git.grassecon.net/grassrootseconomics/visedriver/testutil/testservice" - "git.grassecon.net/grassrootseconomics/visedriver/testutil/testtag" - testdataloader "github.com/peteole/testdata-loader" - "git.grassecon.net/grassrootseconomics/visedriver/remote" -) - -var ( - logg = logging.NewVanilla() - baseDir = testdataloader.GetBasePath() - scriptDir = path.Join(baseDir, "services", "registration") - setDbType string - setConnStr string - setDbSchema string -) - -func init() { - initializers.LoadEnvVariablesPath(baseDir) - config.LoadConfig() -} - -// SetDatabase updates the database used by TestEngine -func SetDatabase(database, connStr, dbSchema string) { - setDbType = database - setConnStr = connStr - setDbSchema = dbSchema -} - -// CleanDatabase removes all test data from the database -func CleanDatabase() { - if setDbType == "postgres" { - ctx := context.Background() - // Update the connection string with the new search path - updatedConnStr, err := updateSearchPath(setConnStr, setDbSchema) - if err != nil { - log.Fatalf("Failed to update search path: %v", err) - } - - dbConn, err := pgxpool.New(ctx, updatedConnStr) - if err != nil { - log.Fatalf("Failed to connect to database for cleanup: %v", err) - } - defer dbConn.Close() - - query := fmt.Sprintf("DELETE FROM %s.kv_vise;", setDbSchema) - _, execErr := dbConn.Exec(ctx, query) - if execErr != nil { - log.Printf("Failed to cleanup table %s.kv_vise: %v", setDbSchema, execErr) - } else { - log.Printf("Successfully cleaned up table %s.kv_vise", setDbSchema) - } - } else { - setConnStr, _ := filepath.Abs(setConnStr) - if err := os.RemoveAll(setConnStr); err != nil { - log.Fatalf("Failed to delete state store %s: %v", setConnStr, err) - } - } -} - -// updateSearchPath updates the search_path (schema) to be used in the connection -func updateSearchPath(connStr string, newSearchPath string) (string, error) { - u, err := url.Parse(connStr) - if err != nil { - return "", fmt.Errorf("invalid connection string: %w", err) - } - - // Parse the query parameters - q := u.Query() - - // Update or add the search_path parameter - q.Set("search_path", newSearchPath) - - // Rebuild the connection string with updated parameters - u.RawQuery = q.Encode() - - return u.String(), nil -} - -func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { - var err error - ctx := context.Background() - ctx = context.WithValue(ctx, "SessionId", sessionId) - pfp := path.Join(scriptDir, "pp.csv") - - var eventChannel = make(chan bool) - - cfg := engine.Config{ - Root: "root", - SessionId: sessionId, - OutputSize: uint32(160), - FlagCount: uint32(128), - } - - if setDbType == "postgres" { - setConnStr = config.DbConn - setConnStr, err = updateSearchPath(setConnStr, setDbSchema) - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } - } else { - setConnStr, err = filepath.Abs(setConnStr) - if err != nil { - fmt.Fprintf(os.Stderr, "connstr err: %v", err) - os.Exit(1) - } - } - - conn, err := storage.ToConnData(setConnStr) - if err != nil { - fmt.Fprintf(os.Stderr, "connstr parse err: %v", err) - os.Exit(1) - } - - resourceDir := scriptDir - menuStorageService := storage.NewMenuStorageService(conn, resourceDir) - - rs, err := menuStorageService.GetResource(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, "resource error: %v", err) - os.Exit(1) - } - - pe, err := menuStorageService.GetPersister(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, "persister error: %v", err) - os.Exit(1) - } - - userDataStore, err := menuStorageService.GetUserdataDb(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, "userdb error: %v", err) - os.Exit(1) - } - - dbResource, ok := rs.(*resource.DbResource) - if !ok { - fmt.Fprintf(os.Stderr, "dbresource cast error") - os.Exit(1) - } - - lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) - lhs.SetDataStore(&userDataStore) - lhs.SetPersister(pe) - - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - - if testtag.AccountService == nil { - testtag.AccountService = &remote.AccountService{} - } - - switch testtag.AccountService.(type) { - case *testservice.TestAccountService: - go func() { - eventChannel <- false - }() - case *remote.AccountService: - go func() { - time.Sleep(5 * time.Second) // Wait for 5 seconds - eventChannel <- true - }() - default: - panic("Unknown account service type") - } - - hl, err := lhs.GetHandler(testtag.AccountService) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - - en := lhs.GetEngine() - en = en.WithFirst(hl.Init) - cleanFn := func() { - err := en.Finish() - if err != nil { - logg.Errorf(err.Error()) - } - - err = menuStorageService.Close() - if err != nil { - logg.Errorf(err.Error()) - } - logg.Infof("testengine storage closed") - } - return en, cleanFn, eventChannel -} diff --git a/testutil/engine_test.go b/testutil/engine_test.go deleted file mode 100644 index f747468..0000000 --- a/testutil/engine_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package testutil - -import ( - "testing" -) - -func TestCreateEngine(t *testing.T) { - o, clean, eventC := TestEngine("foo") - defer clean() - defer func() { - <-eventC - close(eventC) - }() - _ = o -}