446 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			446 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package menutraversaltest
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"flag"
 | |
| 	"log"
 | |
| 	"math/rand"
 | |
| 	"regexp"
 | |
| 	"testing"
 | |
| 
 | |
| 	"git.defalsify.org/vise.git/logging"
 | |
| 	"git.grassecon.net/grassrootseconomics/sarafu-vise/testutil"
 | |
| 	"git.grassecon.net/grassrootseconomics/visedriver/testutil/driver"
 | |
| 	"github.com/gofrs/uuid"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	logg               = logging.NewVanilla().WithDomain("menutraversaltest")
 | |
| 	testData           = driver.ReadData()
 | |
| 	sessionID          string
 | |
| 	src                = rand.NewSource(42)
 | |
| 	g                  = rand.New(src)
 | |
| 	secondarySessionId = "+254700000000"
 | |
| )
 | |
| 
 | |
| var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests")
 | |
| 
 | |
| 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: <amount> <symbol>" 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]) // "<amount> <symbol>"
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| // Extracts the Maximum amount value from the engine response.
 | |
| func extractMaxAmount(response []byte) string {
 | |
| 	// Regex to match "Maximum amount: <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]) // "<amount>"
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| func extractRemainingAttempts(response []byte) string {
 | |
| 	// Regex to match "You have: <number> remaining attempt(s)"
 | |
| 	re := regexp.MustCompile(`(?m)You have:\s+(\d+)\s+remaining attempt\(s\)`)
 | |
| 	match := re.FindSubmatch(response)
 | |
| 	if match != nil {
 | |
| 		return string(match[1]) // "<number>" of remaining attempts
 | |
| 	}
 | |
| 	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) {
 | |
| 	sessionID = GenerateSessionId()
 | |
| 	// 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 i, step := range group.Steps {
 | |
| 				logg.TraceCtxf(ctx, "executing step", "i", i, "step", step)
 | |
| 				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 TestSecondaryAccount(t *testing.T) {
 | |
| 	en, fn, eventChannel, _, _ := testutil.TestEngine(secondarySessionId)
 | |
| 	defer fn()
 | |
| 	ctx := context.Background()
 | |
| 	sessions := testData
 | |
| 	for _, session := range sessions {
 | |
| 		groups := driver.FilterGroupsByName(session.Groups, "account_creation_successful")
 | |
| 		for _, group := range groups {
 | |
| 			for i, step := range group.Steps {
 | |
| 				logg.TraceCtxf(ctx, "executing step", "i", i, "step", step)
 | |
| 				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 i, step := range group.Steps {
 | |
| 				logg.TraceCtxf(ctx, "executing step", "i", i, "step", step)
 | |
| 				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 i, step := range group.Steps {
 | |
| 				logg.TraceCtxf(ctx, "executing step", "i", i, "step", step)
 | |
| 				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, _, pe, flagParser := testutil.TestEngine(sessionID)
 | |
| 	defer fn()
 | |
| 	ctx := context.Background()
 | |
| 
 | |
| 	flag_admin_privilege, _ := flagParser.GetFlag("flag_admin_privilege")
 | |
| 
 | |
| 	// 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)
 | |
| 			attempts := extractRemainingAttempts(b)
 | |
| 
 | |
| 			st := pe.GetState()
 | |
| 
 | |
| 			if st != nil {
 | |
| 				st.SetFlag(flag_admin_privilege)
 | |
| 				if tt.Name == "menu_my_account_reset_others_pin_with_no_privileges" {
 | |
| 					st.ResetFlag(flag_admin_privilege)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			expectedContent := []byte(tt.ExpectedContent)
 | |
| 			expectedContent = bytes.Replace(expectedContent, []byte("{balance}"), []byte(balance), -1)
 | |
| 			expectedContent = bytes.Replace(expectedContent, []byte("{attempts}"), []byte(attempts), -1)
 | |
| 			expectedContent = bytes.Replace(expectedContent, []byte("{secondary_session_id}"), []byte(secondarySessionId), -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)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |