Compare commits

..

1 Commits

Author SHA1 Message Date
alfred-mk
02d43a8c09 added the WIP restart_state devtool 2024-12-31 10:49:37 +03:00
22 changed files with 200 additions and 404 deletions

View File

@@ -1,33 +0,0 @@
package common
import (
"regexp"
"golang.org/x/crypto/bcrypt"
)
// Define the regex pattern as a constant
const (
pinPattern = `^\d{4}$`
)
// checks whether the given input is a 4 digit number
func IsValidPIN(pin string) bool {
match, _ := regexp.MatchString(pinPattern, pin)
return match
}
// HashPIN uses bcrypt with 8 salt rounds to hash the PIN
func HashPIN(pin string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(pin), 8)
if err != nil {
return "", err
}
return string(hash), nil
}
// VerifyPIN compareS the hashed PIN with the plaintext PIN
func VerifyPIN(hashedPIN, pin string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPIN), []byte(pin))
return err == nil
}

View File

@@ -1,173 +0,0 @@
package common
import (
"testing"
"golang.org/x/crypto/bcrypt"
)
func TestIsValidPIN(t *testing.T) {
tests := []struct {
name string
pin string
expected bool
}{
{
name: "Valid PIN with 4 digits",
pin: "1234",
expected: true,
},
{
name: "Valid PIN with leading zeros",
pin: "0001",
expected: true,
},
{
name: "Invalid PIN with less than 4 digits",
pin: "123",
expected: false,
},
{
name: "Invalid PIN with more than 4 digits",
pin: "12345",
expected: false,
},
{
name: "Invalid PIN with letters",
pin: "abcd",
expected: false,
},
{
name: "Invalid PIN with special characters",
pin: "12@#",
expected: false,
},
{
name: "Empty PIN",
pin: "",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := IsValidPIN(tt.pin)
if actual != tt.expected {
t.Errorf("IsValidPIN(%q) = %v; expected %v", tt.pin, actual, tt.expected)
}
})
}
}
func TestHashPIN(t *testing.T) {
tests := []struct {
name string
pin string
}{
{
name: "Valid PIN with 4 digits",
pin: "1234",
},
{
name: "Valid PIN with leading zeros",
pin: "0001",
},
{
name: "Empty PIN",
pin: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hashedPIN, err := HashPIN(tt.pin)
if err != nil {
t.Errorf("HashPIN(%q) returned an error: %v", tt.pin, err)
return
}
if hashedPIN == "" {
t.Errorf("HashPIN(%q) returned an empty hash", tt.pin)
}
// Ensure the hash can be verified with bcrypt
err = bcrypt.CompareHashAndPassword([]byte(hashedPIN), []byte(tt.pin))
if tt.pin != "" && err != nil {
t.Errorf("HashPIN(%q) produced a hash that does not match: %v", tt.pin, err)
}
})
}
}
func TestVerifyMigratedHashPin(t *testing.T) {
tests := []struct {
pin string
hash string
}{
{
pin: "1234",
hash: "$2b$08$dTvIGxCCysJtdvrSnaLStuylPoOS/ZLYYkxvTeR5QmTFY3TSvPQC6",
},
}
for _, tt := range tests {
t.Run(tt.pin, func(t *testing.T) {
ok := VerifyPIN(tt.hash, tt.pin)
if !ok {
t.Errorf("VerifyPIN could not verify migrated PIN: %v", tt.pin)
}
})
}
}
func TestVerifyPIN(t *testing.T) {
tests := []struct {
name string
pin string
hashedPIN string
shouldPass bool
}{
{
name: "Valid PIN verification",
pin: "1234",
hashedPIN: hashPINHelper("1234"),
shouldPass: true,
},
{
name: "Invalid PIN verification with incorrect PIN",
pin: "5678",
hashedPIN: hashPINHelper("1234"),
shouldPass: false,
},
{
name: "Invalid PIN verification with empty PIN",
pin: "",
hashedPIN: hashPINHelper("1234"),
shouldPass: false,
},
{
name: "Invalid PIN verification with invalid hash",
pin: "1234",
hashedPIN: "invalidhash",
shouldPass: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := VerifyPIN(tt.hashedPIN, tt.pin)
if result != tt.shouldPass {
t.Errorf("VerifyPIN(%q, %q) = %v; expected %v", tt.hashedPIN, tt.pin, result, tt.shouldPass)
}
})
}
}
// Helper function to hash a PIN for testing purposes
func hashPINHelper(pin string) string {
hashedPIN, err := HashPIN(pin)
if err != nil {
panic("Failed to hash PIN for test setup: " + err.Error())
}
return hashedPIN
}

View File

@@ -11,9 +11,13 @@ import (
func init() {
DebugCap |= 1
dbTypStr[db.DATATYPE_STATE] = "internal state"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account created"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name"
dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth"

View File

@@ -0,0 +1,76 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"path"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/storage"
)
var (
logg = logging.NewVanilla()
scriptDir = path.Join("services", "registration")
)
func init() {
initializers.LoadEnvVariables()
}
func main() {
config.LoadConfig()
var dbDir string
var sessionId string
var database string
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&database, "db", "gdbm", "database to be used")
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
flag.Parse()
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId)
ctx = context.WithValue(ctx, "Database", database)
resourceDir := scriptDir
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
err := menuStorageService.EnsureDbDir()
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)
}
st := pe.GetState()
if st == nil {
logg.ErrorCtxf(ctx, "state fail in devtool", "state", st)
fmt.Println("cannot get state")
os.Exit(1)
}
fmt.Println("The state:", st)
// set empty Code to allow the menu to run from the top
st.Code = []byte{}
err = pe.Save(sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed to persist the state and cache", "error", err)
os.Exit(1)
}
os.Exit(0)
}

View File

@@ -11,7 +11,6 @@ import (
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/debug"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/logging"
)
@@ -48,14 +47,13 @@ func main() {
store, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "get userdata db: %v\n", err.Error())
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
store.SetPrefix(db.DATATYPE_USERDATA)
d, err := store.Dump(ctx, []byte(sessionId))
if err != nil {
fmt.Fprintf(os.Stderr, "store dump fail: %v\n", err.Error())
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
@@ -69,7 +67,7 @@ func main() {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
fmt.Printf("%vValue: %v\n\n", o, string(v))
fmt.Printf("%vValue: %v\n\n", o, v)
}
err = store.Close()

2
go.mod
View File

@@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd
go 1.23.0
require (
git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9
git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80
github.com/alecthomas/assert/v2 v2.2.2
github.com/gofrs/uuid v4.4.0+incompatible
github.com/grassrootseconomics/eth-custodial v1.3.0-beta

4
go.sum
View File

@@ -1,5 +1,5 @@
git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9 h1:O3m+NgWDWtJm8OculT99c4bDMAO4xLe2c8hpCKpsd9g=
git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 h1:GYUVXRUtMpA40T4COeAduoay6CIgXjD5cfDYZOTFIKw=
git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=

View File

@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"path"
"regexp"
"strconv"
"strings"
@@ -33,6 +34,17 @@ var (
translationDir = path.Join(scriptDir, "locale")
)
// Define the regex patterns as constants
const (
pinPattern = `^\d{4}$`
)
// checks whether the given input is a 4 digit number
func isValidPIN(pin string) bool {
match, _ := regexp.MatchString(pinPattern, pin)
return match
}
// FlagManager handles centralized flag management
type FlagManager struct {
parser *asm.FlagParser
@@ -117,11 +129,6 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource
h.st = h.pe.GetState()
h.ca = h.pe.GetMemory()
if len(input) == 0 {
// move to the top node
h.st.Code = []byte{}
}
sessionId, _ := ctx.Value("SessionId").(string)
flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege")
@@ -269,7 +276,7 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (
flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin")
pinInput := string(input)
// Validate that the PIN is a 4-digit number.
if common.IsValidPIN(pinInput) {
if isValidPIN(pinInput) {
res.FlagSet = append(res.FlagSet, flag_valid_pin)
} else {
res.FlagReset = append(res.FlagReset, flag_valid_pin)
@@ -294,7 +301,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
accountPIN := string(input)
// Validate that the PIN is a 4-digit number.
if !common.IsValidPIN(accountPIN) {
if !isValidPIN(accountPIN) {
res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
return res, nil
}
@@ -356,20 +363,11 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
} else {
res.FlagSet = append(res.FlagSet, flag_pin_mismatch)
return res, nil
}
// Hash the PIN
hashedPIN, err := common.HashPIN(string(temporaryPin))
// If matched, save the confirmed PIN as the new account PIN
err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin))
if err != nil {
logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err)
return res, err
}
// save the hashed PIN as the new account PIN
err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", common.DATA_ACCOUNT_PIN, "hashedPIN value", hashedPIN, "error", err)
logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err)
return res, err
}
return res, nil
@@ -401,19 +399,11 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte
res.FlagSet = append(res.FlagSet, flag_pin_set)
} else {
res.FlagSet = []uint32{flag_pin_mismatch}
return res, nil
}
// Hash the PIN
hashedPIN, err := common.HashPIN(string(temporaryPin))
err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin))
if err != nil {
logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err)
return res, err
}
err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", common.DATA_ACCOUNT_PIN, "value", hashedPIN, "error", err)
logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err)
return res, err
}
@@ -727,7 +717,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res
return res, err
}
if len(input) == 4 {
if common.VerifyPIN(string(AccountPin), string(input)) {
if bytes.Equal(input, AccountPin) {
if h.st.MatchFlag(flag_account_authorized, false) {
res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized)
@@ -954,15 +944,7 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte)
logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err)
return res, err
}
// Hash the PIN
hashedPIN, err := common.HashPIN(string(temporaryPin))
if err != nil {
logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err)
return res, err
}
err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(hashedPIN))
err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin))
if err != nil {
return res, nil
}
@@ -1413,6 +1395,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input
defaultValue = "Not Provided"
}
sm, _ := h.st.Where()
parts := strings.SplitN(sm, "_", 2)
filename := parts[1]

View File

@@ -8,7 +8,6 @@ import (
"strings"
"testing"
"git.defalsify.org/vise.git/cache"
"git.defalsify.org/vise.git/lang"
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
@@ -16,7 +15,6 @@ import (
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/internal/testutil/mocks"
"git.grassecon.net/urdt/ussd/internal/testutil/testservice"
"git.grassecon.net/urdt/ussd/internal/utils"
"git.grassecon.net/urdt/ussd/models"
"git.grassecon.net/urdt/ussd/common"
@@ -121,102 +119,6 @@ func TestNewHandlers(t *testing.T) {
})
}
func TestInit(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
fm, err := NewFlagManager(flagsPath)
if err != nil {
t.Fatal(err.Error())
}
adminstore, err := utils.NewAdminStore(ctx, "admin_numbers")
if err != nil {
t.Fatal(err.Error())
}
st := state.NewState(128)
ca := cache.NewCache()
flag_admin_privilege, _ := fm.GetFlag("flag_admin_privilege")
tests := []struct {
name string
setup func() (*Handlers, context.Context)
input []byte
expectedResult resource.Result
}{
{
name: "Handler not ready",
setup: func() (*Handlers, context.Context) {
return &Handlers{}, ctx
},
input: []byte("1"),
expectedResult: resource.Result{},
},
{
name: "State and memory initialization",
setup: func() (*Handlers, context.Context) {
pe := persist.NewPersister(store).WithSession(sessionId).WithContent(st, ca)
h := &Handlers{
flagManager: fm.parser,
adminstore: adminstore,
pe: pe,
}
return h, context.WithValue(ctx, "SessionId", sessionId)
},
input: []byte("1"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_admin_privilege},
},
},
{
name: "Non-admin session initialization",
setup: func() (*Handlers, context.Context) {
pe := persist.NewPersister(store).WithSession("0712345678").WithContent(st, ca)
h := &Handlers{
flagManager: fm.parser,
adminstore: adminstore,
pe: pe,
}
return h, context.WithValue(context.Background(), "SessionId", "0712345678")
},
input: []byte("1"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_admin_privilege},
},
},
{
name: "Move to top node on empty input",
setup: func() (*Handlers, context.Context) {
pe := persist.NewPersister(store).WithSession(sessionId).WithContent(st, ca)
h := &Handlers{
flagManager: fm.parser,
adminstore: adminstore,
pe: pe,
}
st.Code = []byte("some pending bytecode")
return h, context.WithValue(ctx, "SessionId", sessionId)
},
input: []byte(""),
expectedResult: resource.Result{
FlagReset: []uint32{flag_admin_privilege},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h, testCtx := tt.setup()
res, err := h.Init(testCtx, "", tt.input)
assert.NoError(t, err, "Unexpected error occurred")
assert.Equal(t, res, tt.expectedResult, "Expected result should match actual result")
})
}
}
func TestCreateAccount(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
@@ -1047,14 +949,7 @@ func TestAuthorize(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Hash the PIN
hashedPIN, err := common.HashPIN(accountPIN)
if err != nil {
logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err)
t.Fatal(err)
}
err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN))
err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(accountPIN))
if err != nil {
t.Fatal(err)
}
@@ -1506,6 +1401,59 @@ func TestQuit(t *testing.T) {
}
}
func TestIsValidPIN(t *testing.T) {
tests := []struct {
name string
pin string
expected bool
}{
{
name: "Valid PIN with 4 digits",
pin: "1234",
expected: true,
},
{
name: "Valid PIN with leading zeros",
pin: "0001",
expected: true,
},
{
name: "Invalid PIN with less than 4 digits",
pin: "123",
expected: false,
},
{
name: "Invalid PIN with more than 4 digits",
pin: "12345",
expected: false,
},
{
name: "Invalid PIN with letters",
pin: "abcd",
expected: false,
},
{
name: "Invalid PIN with special characters",
pin: "12@#",
expected: false,
},
{
name: "Empty PIN",
pin: "",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := isValidPIN(tt.pin)
if actual != tt.expected {
t.Errorf("isValidPIN(%q) = %v; expected %v", tt.pin, actual, tt.expected)
}
})
}
}
func TestValidateAmount(t *testing.T) {
fm, err := NewFlagManager(flagsPath)
if err != nil {
@@ -1752,7 +1700,7 @@ func TestGetProfile(t *testing.T) {
result: resource.Result{
Content: fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
"John Doee", "Male", "49", "Kilifi", "Bananas",
"John Doee", "Male", "48", "Kilifi", "Bananas",
),
},
},
@@ -1764,7 +1712,7 @@ func TestGetProfile(t *testing.T) {
result: resource.Result{
Content: fmt.Sprintf(
"Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n",
"John Doee", "Male", "49", "Kilifi", "Bananas",
"John Doee", "Male", "48", "Kilifi", "Bananas",
),
},
},
@@ -1776,7 +1724,7 @@ func TestGetProfile(t *testing.T) {
result: resource.Result{
Content: fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
"John Doee", "Male", "49", "Kilifi", "Bananas",
"John Doee", "Male", "48", "Kilifi", "Bananas",
),
},
},

View File

@@ -17,7 +17,8 @@ var (
type DefaultRequestParser struct {
}
func (rp *DefaultRequestParser) GetSessionId(rq any) (string, error) {
func(rp *DefaultRequestParser) GetSessionId(rq any) (string, error) {
rqv, ok := rq.(*http.Request)
if !ok {
return "", handlers.ErrInvalidRequest
@@ -29,7 +30,7 @@ func (rp *DefaultRequestParser) GetSessionId(rq any) (string, error) {
return v, nil
}
func (rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) {
func(rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) {
rqv, ok := rq.(*http.Request)
if !ok {
return nil, handlers.ErrInvalidRequest
@@ -52,24 +53,25 @@ func ToSessionHandler(h handlers.RequestHandler) *SessionHandler {
}
}
func (f *SessionHandler) writeError(w http.ResponseWriter, code int, err error) {
func(f *SessionHandler) writeError(w http.ResponseWriter, code int, err error) {
s := err.Error()
w.Header().Set("Content-Length", strconv.Itoa(len(s)))
w.WriteHeader(code)
_, err = w.Write([]byte(s))
_, err = w.Write([]byte{})
if err != nil {
logg.Errorf("error writing error!!", "err", err, "olderr", s)
w.WriteHeader(500)
}
return
}
func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func(f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var code int
var err error
var perr error
rqs := handlers.RequestSession{
Ctx: req.Context(),
Ctx: req.Context(),
Writer: w,
}

View File

@@ -62,10 +62,10 @@
},
{
"input": "1234",
"expectedContent": "Select language:\n1:English\n2:Kiswahili"
"expectedContent": "Select language:\n0:English\n1:Kiswahili"
},
{
"input": "1",
"input": "0",
"expectedContent": "Your language change request was successful.\n0:Back\n9:Quit"
},
{
@@ -430,7 +430,7 @@
},
{
"input": "1234",
"expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back\n9:Quit"
"expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back"
},
{
"input": "0",

View File

@@ -298,10 +298,9 @@ func TestMainMenuSend(t *testing.T) {
ctx := context.Background()
sessions := testData
for _, session := range sessions {
groups := driver.FilterGroupsByName(session.Groups, "send_with_invite")
groups := driver.FilterGroupsByName(session.Groups, "send_with_invalid_inputs")
for _, group := range groups {
for index, step := range group.Steps {
t.Logf("step %v with input %v", index, step.Input)
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)

View File

@@ -7,14 +7,14 @@
"steps": [
{
"input": "",
"expectedContent": "Welcome to Sarafu Network\nPlease select a language\n1:English\n2:Kiswahili"
"expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:English\n1:Kiswahili"
},
{
"input": "1",
"expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n1:Yes\n2:No"
"input": "0",
"expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No"
},
{
"input": "1",
"input": "0",
"expectedContent": "Please enter a new four number PIN for your account:\n0:Exit"
},
{
@@ -40,14 +40,14 @@
"steps": [
{
"input": "",
"expectedContent": "Welcome to Sarafu Network\nPlease select a language\n1:English\n2:Kiswahili"
"expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:English\n1:Kiswahili"
},
{
"input": "0",
"expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No"
},
{
"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!"
}
]
@@ -64,8 +64,8 @@
"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": "000",
"expectedContent": "000 is invalid, please try again:\n1:Retry\n9:Quit"
},
{
"input": "1",

View File

@@ -7,4 +7,3 @@ HALT
INCMP _ 0
INCMP my_balance 1
INCMP community_balance 2
INCMP . *

View File

@@ -2,9 +2,9 @@ LOAD reset_account_authorized 0
LOAD reset_incorrect 0
CATCH incorrect_pin flag_incorrect_pin 1
CATCH pin_entry flag_account_authorized 0
MOUT english 1
MOUT kiswahili 2
MOUT english 0
MOUT kiswahili 1
HALT
INCMP set_default 1
INCMP set_swa 2
INCMP set_default 0
INCMP set_swa 1
INCMP . *

View File

@@ -9,4 +9,3 @@ MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9
INCMP . *

View File

@@ -20,4 +20,3 @@ INCMP edit_yob 4
INCMP edit_location 5
INCMP edit_offerings 6
INCMP view_profile 7
INCMP . *

View File

@@ -14,4 +14,3 @@ INCMP balances 3
INCMP check_statement 4
INCMP pin_management 5
INCMP address 6
INCMP . *

View File

@@ -9,4 +9,3 @@ MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9
INCMP . *

View File

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

View File

@@ -1,5 +1,5 @@
MOUT yes 1
MOUT no 2
MOUT yes 0
MOUT no 1
HALT
INCMP create_pin 1
INCMP create_pin 0
INCMP quit *

View File

@@ -4,8 +4,5 @@ LOAD reset_incorrect 6
CATCH incorrect_pin flag_incorrect_pin 1
CATCH pin_entry flag_account_authorized 0
MOUT back 0
MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9
INCMP . *