forked from grassrootseconomics/visedriver
		
	Merge pull request 'menu-traversal-v2' (#115) from menu-traversal-v2 into master
Reviewed-on: urdt/ussd#115 Reviewed-by: lash <accounts-grassrootseconomics@holbrook.no>
This commit is contained in:
		
						commit
						113f1a5b34
					
				| @ -17,6 +17,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	httpserver "git.grassecon.net/urdt/ussd/internal/http" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| ) | ||||
| @ -127,7 +128,8 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler() | ||||
| 	accountService := server.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
|  | ||||
| @ -14,6 +14,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| ) | ||||
| 
 | ||||
| @ -94,8 +95,9 @@ func main() { | ||||
| 
 | ||||
| 	lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) | ||||
| 	lhs.SetDataStore(&userdataStore) | ||||
| 	accountService := server.AccountService{} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler() | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
|  | ||||
| @ -16,6 +16,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	httpserver "git.grassecon.net/urdt/ussd/internal/http" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| ) | ||||
| @ -87,8 +88,8 @@ func main() { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler() | ||||
| 	accountService := server.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
|  | ||||
| @ -11,6 +11,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| ) | ||||
| 
 | ||||
| @ -85,7 +86,8 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler() | ||||
| 	accountService := server.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
|  | ||||
							
								
								
									
										111
									
								
								driver/groupdriver.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								driver/groupdriver.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | ||||
| package driver | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| ) | ||||
| 
 | ||||
| type Step struct { | ||||
| 	Input           string `json:"input"` | ||||
| 	ExpectedContent string `json:"expectedContent"` | ||||
| } | ||||
| 
 | ||||
| func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { | ||||
| 	pattern := regexp.QuoteMeta(s.ExpectedContent) | ||||
| 	re, err := regexp.Compile(pattern) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	if re.Match([]byte(content)) { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	return false, nil | ||||
| } | ||||
| 
 | ||||
| // Group represents a group of steps
 | ||||
| type Group struct { | ||||
| 	Name  string `json:"name"` | ||||
| 	Steps []Step `json:"steps"` | ||||
| } | ||||
| 
 | ||||
| type TestCase struct { | ||||
| 	Name            string | ||||
| 	Input           string | ||||
| 	ExpectedContent string | ||||
| } | ||||
| 
 | ||||
| func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) { | ||||
| 	pattern := regexp.QuoteMeta(s.ExpectedContent) | ||||
| 	re, err := regexp.Compile(pattern) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	// Check if the content matches the regex pattern
 | ||||
| 	if re.Match(content) { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	return false, nil | ||||
| } | ||||
| 
 | ||||
| // DataGroup represents the overall structure of the JSON.
 | ||||
| type DataGroup struct { | ||||
| 	Groups []Group `json:"groups"` | ||||
| } | ||||
| 
 | ||||
| type Session struct { | ||||
| 	Name   string  `json:"name"` | ||||
| 	Groups []Group `json:"groups"` | ||||
| } | ||||
| 
 | ||||
| func ReadData() []Session { | ||||
| 	data, err := os.ReadFile("test_setup.json") | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("Failed to read file: %v", err) | ||||
| 	} | ||||
| 	// Unmarshal JSON data
 | ||||
| 	var sessions []Session | ||||
| 	err = json.Unmarshal(data, &sessions) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("Failed to unmarshal JSON: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return sessions | ||||
| } | ||||
| 
 | ||||
| func FilterGroupsByName(groups []Group, name string) []Group { | ||||
| 	var filteredGroups []Group | ||||
| 	for _, group := range groups { | ||||
| 		if group.Name == name { | ||||
| 			filteredGroups = append(filteredGroups, group) | ||||
| 		} | ||||
| 	} | ||||
| 	return filteredGroups | ||||
| } | ||||
| 
 | ||||
| func LoadTestGroups(filePath string) (DataGroup, error) { | ||||
| 	var sessionsData DataGroup | ||||
| 	data, err := os.ReadFile(filePath) | ||||
| 	if err != nil { | ||||
| 		return sessionsData, err | ||||
| 	} | ||||
| 	err = json.Unmarshal(data, &sessionsData) | ||||
| 	return sessionsData, err | ||||
| } | ||||
| 
 | ||||
| func CreateTestCases(group DataGroup) []TestCase { | ||||
| 	var tests []TestCase | ||||
| 	for _, group := range group.Groups { | ||||
| 		for _, step := range group.Steps { | ||||
| 			// Create a test case for each group
 | ||||
| 			tests = append(tests, TestCase{ | ||||
| 				Name:            group.Name, | ||||
| 				Input:           step.Input, | ||||
| 				ExpectedContent: step.ExpectedContent, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return tests | ||||
| } | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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 | ||||
|  | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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= | ||||
|  | ||||
| @ -6,6 +6,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/engine" | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/ussd" | ||||
| ) | ||||
| 
 | ||||
| @ -52,8 +53,8 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { | ||||
| 	ls.UserdataStore = db | ||||
| } | ||||
| 
 | ||||
| func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) { | ||||
| 	ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore) | ||||
| func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { | ||||
| 	ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| @ -4,6 +4,7 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| @ -18,6 +19,9 @@ type AccountServiceInterface interface { | ||||
| type AccountService struct { | ||||
| } | ||||
| 
 | ||||
| type TestAccountService struct { | ||||
| } | ||||
| 
 | ||||
| // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID.
 | ||||
| //
 | ||||
| // Parameters:
 | ||||
| @ -92,3 +96,58 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { | ||||
| 	} | ||||
| 	return &accountResp, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CreateAccount() (*models.AccountResponse, error) { | ||||
| 	return &models.AccountResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			CustodialId json.Number `json:"custodialId"` | ||||
| 			PublicKey   string      `json:"publicKey"` | ||||
| 			TrackingId  string      `json:"trackingId"` | ||||
| 		}{ | ||||
| 			CustodialId: json.Number("182"), | ||||
| 			PublicKey:   "0x48ADca309b5085852207FAaf2816eD72B52F527C", | ||||
| 			TrackingId:  "28ebe84d-b925-472c-87ae-bbdfa1fb97be", | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { | ||||
| 
 | ||||
| 	balanceResponse := &models.BalanceResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			Balance string      `json:"balance"` | ||||
| 			Nonce   json.Number `json:"nonce"` | ||||
| 		}{ | ||||
| 			Balance: "0.003 CELO", | ||||
| 			Nonce:   json.Number("0"), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	return balanceResponse, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { | ||||
| 	trackResponse := &models.TrackStatusResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			Transaction struct { | ||||
| 				CreatedAt     time.Time   "json:\"createdAt\"" | ||||
| 				Status        string      "json:\"status\"" | ||||
| 				TransferValue json.Number "json:\"transferValue\"" | ||||
| 				TxHash        string      "json:\"txHash\"" | ||||
| 				TxType        string      "json:\"txType\"" | ||||
| 			} | ||||
| 		}{ | ||||
| 			Transaction: models.Transaction{ | ||||
| 				CreatedAt:     time.Now(), | ||||
| 				Status:        "SUCCESS", | ||||
| 				TransferValue: json.Number("0.5"), | ||||
| 				TxHash:        "0x123abc456def", | ||||
| 				TxType:        "transfer", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return trackResponse, nil | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -15,6 +15,7 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.defalsify.org/vise.git/state" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/mocks" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| @ -28,22 +29,15 @@ var ( | ||||
| 	flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") | ||||
| ) | ||||
| 
 | ||||
| type Transaction struct { | ||||
| 	CreatedAt     time.Time   `json:"createdAt"` | ||||
| 	Status        string      `json:"status"` | ||||
| 	TransferValue json.Number `json:"transferValue"` | ||||
| 	TxHash        string      `json:"txHash"` | ||||
| 	TxType        string      `json:"txType"` | ||||
| } | ||||
| 
 | ||||
| func TestNewHandlers(t *testing.T) { | ||||
| 	fm, err := NewFlagManager(flagsPath) | ||||
| 	accountService := server.TestAccountService{} | ||||
| 	if err != nil { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 	t.Run("Valid UserDataStore", func(t *testing.T) { | ||||
| 		mockStore := &mocks.MockUserDataStore{} | ||||
| 		handlers, err := NewHandlers(fm.parser, mockStore) | ||||
| 		handlers, err := NewHandlers(fm.parser, mockStore, &accountService) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("expected no error, got %v", err) | ||||
| 		} | ||||
| @ -59,7 +53,7 @@ func TestNewHandlers(t *testing.T) { | ||||
| 	t.Run("Nil UserDataStore", func(t *testing.T) { | ||||
| 		appFlags := &asm.FlagParser{} | ||||
| 
 | ||||
| 		handlers, err := NewHandlers(appFlags, nil) | ||||
| 		handlers, err := NewHandlers(appFlags, nil, &accountService) | ||||
| 
 | ||||
| 		if err == nil { | ||||
| 			t.Fatal("expected an error, got none") | ||||
| @ -1089,7 +1083,7 @@ func TestCheckAccountStatus(t *testing.T) { | ||||
| 						TxType        string      "json:\"txType\"" | ||||
| 					} | ||||
| 				}{ | ||||
| 					Transaction: Transaction{ | ||||
| 					Transaction: models.Transaction{ | ||||
| 						CreatedAt:     time.Now(), | ||||
| 						Status:        "SUCCESS", | ||||
| 						TransferValue: json.Number("0.5"), | ||||
| @ -1127,7 +1121,7 @@ func TestCheckAccountStatus(t *testing.T) { | ||||
| 						TxType        string      "json:\"txType\"" | ||||
| 					} | ||||
| 				}{ | ||||
| 					Transaction: Transaction{ | ||||
| 					Transaction: models.Transaction{ | ||||
| 						CreatedAt:     time.Now(), | ||||
| 						Status:        "IN_NETWORK", | ||||
| 						TransferValue: json.Number("0.5"), | ||||
|  | ||||
| @ -5,6 +5,13 @@ import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type Transaction struct { | ||||
| 	CreatedAt     time.Time   `json:"createdAt"` | ||||
| 	Status        string      `json:"status"` | ||||
| 	TransferValue json.Number `json:"transferValue"` | ||||
| 	TxHash        string      `json:"txHash"` | ||||
| 	TxType        string      `json:"txType"` | ||||
| } | ||||
| 
 | ||||
| type TrackStatusResponse struct { | ||||
| 	Ok     bool `json:"ok"` | ||||
| @ -17,4 +24,4 @@ type TrackStatusResponse struct { | ||||
| 			TxType        string      `json:"txType"` | ||||
| 		} | ||||
| 	} `json:"result"` | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										121
									
								
								internal/testutil/TestEngine.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								internal/testutil/TestEngine.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| package testutil | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/engine" | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	testdataloader "github.com/peteole/testdata-loader" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	baseDir   = testdataloader.GetBasePath() | ||||
| 	logg      = logging.NewVanilla() | ||||
| 	scriptDir = path.Join(baseDir, "services", "registration") | ||||
| ) | ||||
| 
 | ||||
| func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { | ||||
| 	ctx := context.Background() | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	pfp := path.Join(scriptDir, "pp.csv") | ||||
| 
 | ||||
| 	var eventChannel = make(chan bool) | ||||
| 
 | ||||
| 	cfg := engine.Config{ | ||||
| 		Root:       "root", | ||||
| 		SessionId:  sessionId, | ||||
| 		OutputSize: uint32(160), | ||||
| 		FlagCount:  uint32(128), | ||||
| 	} | ||||
| 
 | ||||
| 	dbDir := ".test_state" | ||||
| 	resourceDir := scriptDir | ||||
| 	menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) | ||||
| 
 | ||||
| 	err := menuStorageService.EnsureDbDir() | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	rs, err := menuStorageService.GetResource(ctx) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	pe, err := menuStorageService.GetPersister(ctx) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	userDataStore, err := menuStorageService.GetUserdataDb(ctx) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	dbResource, ok := rs.(*resource.DbResource) | ||||
| 	if !ok { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) | ||||
| 	lhs.SetDataStore(&userDataStore) | ||||
| 	lhs.SetPersister(pe) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	if AccountService == nil { | ||||
| 		AccountService = &server.AccountService{} | ||||
| 	} | ||||
| 
 | ||||
| 	switch AccountService.(type) { | ||||
| 	case *server.TestAccountService: | ||||
| 		go func() { | ||||
| 			eventChannel <- false | ||||
| 		}() | ||||
| 	case *server.AccountService: | ||||
| 		go func() { | ||||
| 			time.Sleep(5 * time.Second) // Wait for 5 seconds
 | ||||
| 			eventChannel <- true | ||||
| 		}() | ||||
| 	default: | ||||
| 		panic("Unknown account service type") | ||||
| 	} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler(AccountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	en := lhs.GetEngine() | ||||
| 	en = en.WithFirst(hl.Init) | ||||
| 	cleanFn := func() { | ||||
| 		err := en.Finish() | ||||
| 		if err != nil { | ||||
| 			logg.Errorf(err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 		err = menuStorageService.Close() | ||||
| 		if err != nil { | ||||
| 			logg.Errorf(err.Error()) | ||||
| 		} | ||||
| 		logg.Infof("testengine storage closed") | ||||
| 	} | ||||
| 	return en, cleanFn, eventChannel | ||||
| } | ||||
							
								
								
									
										11
									
								
								internal/testutil/offlinetest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								internal/testutil/offlinetest.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| // +build !online
 | ||||
| 
 | ||||
| package testutil | ||||
| 
 | ||||
| import ( | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	AccountService server.AccountServiceInterface = &server.TestAccountService{} | ||||
| ) | ||||
							
								
								
									
										9
									
								
								internal/testutil/onlinetest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/testutil/onlinetest.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| // +build online
 | ||||
| 
 | ||||
| package testutil | ||||
| 
 | ||||
| import "git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 
 | ||||
| var ( | ||||
| 	AccountService server.AccountServiceInterface | ||||
| ) | ||||
							
								
								
									
										460
									
								
								menutraversal_test/group_test.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								menutraversal_test/group_test.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,460 @@ | ||||
| { | ||||
|     "groups": [ | ||||
|         { | ||||
|             "name": "my_account_change_pin", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "5", | ||||
|                     "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n3:Guard my PIN\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Enter your old PIN\n\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Enter a new four number PIN:\n\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Confirm your new PIN:\n\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Your PIN change request has been successful\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_language_change", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "2", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1235", | ||||
|                     "expectedContent": "Incorrect pin\n1:retry\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Select language:\n0:english\n1:kiswahili" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Your language change request was successful.\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_check_my_balance", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1235", | ||||
|                     "expectedContent": "Incorrect pin\n1:retry\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Your balance is 0.003 CELO\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                      "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                      | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_check_community_balance", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "2", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1235", | ||||
|                     "expectedContent": "Incorrect pin\n1:retry\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Your community balance is 0.003 CELO\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                      "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" | ||||
|                      | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_edit_firstname", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Enter your first names:\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "foo", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_edit_familyname", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "2", | ||||
|                     "expectedContent": "Enter family name:\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "bar", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|                 | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_edit_gender", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_edit_yob", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "4", | ||||
|                     "expectedContent": "Enter your year of birth\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1945", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_edit_location", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "5", | ||||
|                     "expectedContent": "Enter your location:\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "Kilifi", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_edit_offerings", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "6", | ||||
|                     "expectedContent": "Enter the services or goods you offer: \n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "Bananas", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             "name": "menu_my_account_view_profile", | ||||
|             "steps": [ | ||||
|                 { | ||||
|                     "input": "", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "3", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "7", | ||||
|                     "expectedContent": "Please enter your PIN:" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "1234", | ||||
|                     "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                 }, | ||||
|                 { | ||||
|                     "input": "0", | ||||
|                     "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|        | ||||
|          | ||||
|         | ||||
|          | ||||
|      | ||||
| 
 | ||||
							
								
								
									
										278
									
								
								menutraversal_test/menu_traversal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								menutraversal_test/menu_traversal_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,278 @@ | ||||
| package menutraversaltest | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"log" | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/driver" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil" | ||||
| 	"github.com/gofrs/uuid" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	testData      = driver.ReadData() | ||||
| 	testStore     = ".test_state" | ||||
| 	groupTestFile = "group_test.json" | ||||
| 	sessionID     string | ||||
| 	src           = rand.NewSource(42) | ||||
| 	g             = rand.New(src) | ||||
| ) | ||||
| 
 | ||||
| func GenerateSessionId() string { | ||||
| 	uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g)) | ||||
| 	v, err := uu.NewV4() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return v.String() | ||||
| } | ||||
| 
 | ||||
| // Extract the public key from the engine response
 | ||||
| func extractPublicKey(response []byte) string { | ||||
| 	// Regex pattern to match the public key starting with 0x and 40 characters
 | ||||
| 	re := regexp.MustCompile(`0x[a-fA-F0-9]{40}`) | ||||
| 	match := re.Find(response) | ||||
| 	if match != nil { | ||||
| 		return string(match) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func TestMain(m *testing.M) { | ||||
| 	sessionID = GenerateSessionId() | ||||
| 	defer func() { | ||||
| 		if err := os.RemoveAll(testStore); err != nil { | ||||
| 			log.Fatalf("Failed to delete state store %s: %v", testStore, err) | ||||
| 		} | ||||
| 	}() | ||||
| 	m.Run() | ||||
| } | ||||
| 
 | ||||
| func TestAccountCreationSuccessful(t *testing.T) { | ||||
| 	en, fn, eventChannel := testutil.TestEngine(sessionID) | ||||
| 	defer fn() | ||||
| 	ctx := context.Background() | ||||
| 	sessions := testData | ||||
| 	for _, session := range sessions { | ||||
| 		groups := driver.FilterGroupsByName(session.Groups, "account_creation_successful") | ||||
| 		for _, group := range groups { | ||||
| 			for _, step := range group.Steps { | ||||
| 				cont, err := en.Exec(ctx, []byte(step.Input)) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) | ||||
| 				} | ||||
| 				if !cont { | ||||
| 					break | ||||
| 				} | ||||
| 				w := bytes.NewBuffer(nil) | ||||
| 				_, err = en.Flush(ctx, w) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) | ||||
| 				} | ||||
| 				b := w.Bytes() | ||||
| 				match, err := step.MatchesExpectedContent(b) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) | ||||
| 				} | ||||
| 				if !match { | ||||
| 					t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	<-eventChannel | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestAccountRegistrationRejectTerms(t *testing.T) { | ||||
| 	// Generate a new UUID for this edge case test
 | ||||
| 	uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g)) | ||||
| 	v, err := uu.NewV4() | ||||
| 	if err != nil { | ||||
| 		t.Fail() | ||||
| 	} | ||||
| 	edgeCaseSessionID := v.String() | ||||
| 	en, fn, _ := testutil.TestEngine(edgeCaseSessionID) | ||||
| 	defer fn() | ||||
| 	ctx := context.Background() | ||||
| 	sessions := testData | ||||
| 	for _, session := range sessions { | ||||
| 		groups := driver.FilterGroupsByName(session.Groups, "account_creation_reject_terms") | ||||
| 		for _, group := range groups { | ||||
| 			for _, step := range group.Steps { | ||||
| 				cont, err := en.Exec(ctx, []byte(step.Input)) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) | ||||
| 					return | ||||
| 				} | ||||
| 				if !cont { | ||||
| 					break | ||||
| 				} | ||||
| 				w := bytes.NewBuffer(nil) | ||||
| 				if _, err := en.Flush(ctx, w); err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) | ||||
| 				} | ||||
| 
 | ||||
| 				b := w.Bytes() | ||||
| 				match, err := step.MatchesExpectedContent(b) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) | ||||
| 				} | ||||
| 				if !match { | ||||
| 					t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestMainMenuHelp(t *testing.T) { | ||||
| 	en, fn, _ := testutil.TestEngine(sessionID) | ||||
| 	defer fn() | ||||
| 	ctx := context.Background() | ||||
| 	sessions := testData | ||||
| 	for _, session := range sessions { | ||||
| 		groups := driver.FilterGroupsByName(session.Groups, "main_menu_help") | ||||
| 		for _, group := range groups { | ||||
| 			for _, step := range group.Steps { | ||||
| 				cont, err := en.Exec(ctx, []byte(step.Input)) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) | ||||
| 					return | ||||
| 				} | ||||
| 				if !cont { | ||||
| 					break | ||||
| 				} | ||||
| 				w := bytes.NewBuffer(nil) | ||||
| 				if _, err := en.Flush(ctx, w); err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) | ||||
| 				} | ||||
| 
 | ||||
| 				b := w.Bytes() | ||||
| 				match, err := step.MatchesExpectedContent(b) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) | ||||
| 				} | ||||
| 				if !match { | ||||
| 					t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestMainMenuQuit(t *testing.T) { | ||||
| 	en, fn, _ := testutil.TestEngine(sessionID) | ||||
| 	defer fn() | ||||
| 	ctx := context.Background() | ||||
| 	sessions := testData | ||||
| 	for _, session := range sessions { | ||||
| 		groups := driver.FilterGroupsByName(session.Groups, "main_menu_quit") | ||||
| 		for _, group := range groups { | ||||
| 			for _, step := range group.Steps { | ||||
| 				cont, err := en.Exec(ctx, []byte(step.Input)) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) | ||||
| 					return | ||||
| 				} | ||||
| 				if !cont { | ||||
| 					break | ||||
| 				} | ||||
| 				w := bytes.NewBuffer(nil) | ||||
| 				if _, err := en.Flush(ctx, w); err != nil { | ||||
| 					t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err) | ||||
| 				} | ||||
| 
 | ||||
| 				b := w.Bytes() | ||||
| 				match, err := step.MatchesExpectedContent(b) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) | ||||
| 				} | ||||
| 				if !match { | ||||
| 					t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", step.ExpectedContent, b) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestMyAccount_MyAddress(t *testing.T) { | ||||
| 	en, fn, _ := testutil.TestEngine(sessionID) | ||||
| 	defer fn() | ||||
| 	ctx := context.Background() | ||||
| 	sessions := testData | ||||
| 	for _, session := range sessions { | ||||
| 		groups := driver.FilterGroupsByName(session.Groups, "menu_my_account_my_address") | ||||
| 		for _, group := range groups { | ||||
| 			for index, step := range group.Steps { | ||||
| 				t.Logf("step %v with input %v", index, step.Input) | ||||
| 				cont, err := en.Exec(ctx, []byte(step.Input)) | ||||
| 				if err != nil { | ||||
| 					t.Errorf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) | ||||
| 					return | ||||
| 				} | ||||
| 				if !cont { | ||||
| 					break | ||||
| 				} | ||||
| 				w := bytes.NewBuffer(nil) | ||||
| 				if _, err := en.Flush(ctx, w); err != nil { | ||||
| 					t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err) | ||||
| 				} | ||||
| 				b := w.Bytes() | ||||
| 
 | ||||
| 				publicKey := extractPublicKey(b) | ||||
| 				expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) | ||||
| 				step.ExpectedContent = string(expectedContent) | ||||
| 				match, err := step.MatchesExpectedContent(b) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) | ||||
| 				} | ||||
| 				if !match { | ||||
| 					t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expectedContent, b) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestGroups(t *testing.T) { | ||||
| 	groups, err := driver.LoadTestGroups(groupTestFile) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("Failed to load test groups: %v", err) | ||||
| 	} | ||||
| 	en, fn, _ := testutil.TestEngine(sessionID) | ||||
| 	defer fn() | ||||
| 	ctx := context.Background() | ||||
| 	// Create test cases from loaded groups
 | ||||
| 	tests := driver.CreateTestCases(groups) | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.Name, func(t *testing.T) { | ||||
| 			cont, err := en.Exec(ctx, []byte(tt.Input)) | ||||
| 			if err != nil { | ||||
| 				t.Errorf("Test case '%s' failed at input '%s': %v", tt.Name, tt.Input, err) | ||||
| 				return | ||||
| 			} | ||||
| 			if !cont { | ||||
| 				return | ||||
| 			} | ||||
| 			w := bytes.NewBuffer(nil) | ||||
| 			if _, err := en.Flush(ctx, w); err != nil { | ||||
| 				t.Errorf("Test case '%s' failed during Flush: %v", tt.Name, err) | ||||
| 			} | ||||
| 			b := w.Bytes() | ||||
| 			match, err := tt.MatchesExpectedContent(b) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("Error compiling regex for step '%s': %v", tt.Input, err) | ||||
| 			} | ||||
| 			if !match { | ||||
| 				t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", tt.ExpectedContent, b) | ||||
| 			} | ||||
| 
 | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										153
									
								
								menutraversal_test/test_setup.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								menutraversal_test/test_setup.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | ||||
| [ | ||||
|     { | ||||
|         "name": "session one", | ||||
|         "groups": [ | ||||
|             { | ||||
|                 "name": "account_creation_successful", | ||||
|                 "steps": [ | ||||
|                     { | ||||
|                         "input": "", | ||||
|                         "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:english\n1:kiswahili" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "0", | ||||
|                         "expectedContent": "Do you agree to terms and conditions?\n0:yes\n1:no" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "0", | ||||
|                         "expectedContent": "Please enter a new four number PIN for your account:\n0:Exit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1234", | ||||
|                         "expectedContent": "Enter your four number PIN again:" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1111", | ||||
|                         "expectedContent": "The PIN is not a match. Try again\n1:retry\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1", | ||||
|                         "expectedContent": "Enter your four number PIN again:" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1234", | ||||
|                         "expectedContent": "Your account is being created...Thank you for using Sarafu. Goodbye!" | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 "name": "account_creation_reject_terms", | ||||
|                 "steps": [ | ||||
|                     { | ||||
|                         "input": "", | ||||
|                         "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:english\n1:kiswahili" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "0", | ||||
|                         "expectedContent": "Do you agree to terms and conditions?\n0:yes\n1:no" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1", | ||||
|                         "expectedContent": "Thank you for using Sarafu. Goodbye!" | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 "name": "send_with_invalid_inputs", | ||||
|                 "steps": [ | ||||
|                     { | ||||
|                         "input": "", | ||||
|                         "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1", | ||||
|                         "expectedContent": "Enter recipient's phone number:\n0:Back" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "000", | ||||
|                         "expectedContent": "000 is not registered or invalid, please try again:\n1:retry\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1", | ||||
|                         "expectedContent": "Enter recipient's phone number:\n0:Back" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "065656", | ||||
|                         "expectedContent": "Maximum amount: 0.003 CELO\nEnter amount:\n0:Back" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "0.1", | ||||
|                         "expectedContent": "Amount 0.1 is invalid, please try again:\n1:retry\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1", | ||||
|                         "expectedContent": "Maximum amount: 0.003 CELO\nEnter amount:\n0:Back" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "0.001", | ||||
|                         "expectedContent": "065656 will receive 0.001 from {public_key}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1222", | ||||
|                         "expectedContent": "Incorrect pin\n1:retry\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1", | ||||
|                         "expectedContent": "065656 will receive 0.001 from {public_key}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "1234", | ||||
|                         "expectedContent": "Your request has been sent. 065656 will receive 0.001 from {public_key}." | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 "name": "main_menu_help", | ||||
|                 "steps": [ | ||||
|                     { | ||||
|                         "input": "", | ||||
|                         "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "4", | ||||
|                         "expectedContent": "For more help,please call: 0757628885" | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 "name": "main_menu_quit", | ||||
|                 "steps": [ | ||||
|                     { | ||||
|                         "input": "", | ||||
|                         "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "9", | ||||
|                         "expectedContent": "Thank you for using Sarafu. Goodbye!" | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 "name": "menu_my_account_my_address", | ||||
|                 "steps": [ | ||||
|                     { | ||||
|                         "input": "", | ||||
|                         "expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "3", | ||||
|                         "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "6", | ||||
|                         "expectedContent": "Address: {public_key}\n9:Quit" | ||||
|                     }, | ||||
|                     { | ||||
|                         "input": "9", | ||||
|                         "expectedContent": "Thank you for using Sarafu. Goodbye!" | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| ] | ||||
| @ -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 | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| Enter a new four number pin | ||||
| Enter a new four number PIN: | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user