Compare commits

..

83 Commits

Author SHA1 Message Date
Carlosokumu
fc85bd7eed return an event channel for the test engine 2024-10-09 14:06:16 +03:00
Carlosokumu
5869324c16 remove unused variable 2024-10-09 14:03:36 +03:00
Carlosokumu
1d67fb694b use regex for vm output and expected content 2024-10-09 12:18:56 +03:00
Carlosokumu
3d409a7a59 remove blank line 2024-10-08 22:44:07 +03:00
Carlosokumu
22047ba703 update account service initialization based on the tag 2024-10-08 22:43:11 +03:00
Carlosokumu
8751c4f5bd use just one driver for the tests 2024-10-08 22:19:48 +03:00
Carlosokumu
fc935535f9 remove deprecated code 2024-10-08 22:19:21 +03:00
Carlosokumu
ef4846b11f move online,offline and test engine into the testutil package 2024-10-08 22:18:44 +03:00
Carlosokumu
827b99e48d Revert "add dummy vouchers list"
This reverts commit 188cb573dd.
2024-10-07 20:36:02 +03:00
Carlosokumu
e09c1e1e98 Revert "add dummy vouchers list"
This reverts commit 188cb573dd.
2024-10-07 20:35:18 +03:00
Carlosokumu
87c6029199 Revert "add dummy vouchers list"
This reverts commit 188cb573dd.
2024-10-07 20:33:02 +03:00
Carlosokumu
992dd0bd54 factor out setup test 2024-10-07 19:05:36 +03:00
Carlosokumu
4cd7c742ef factor out tested groups 2024-10-07 18:36:34 +03:00
Carlosokumu
0d18eec39f remove debug message 2024-10-07 16:55:07 +03:00
Carlosokumu
bb009fa1e2 add group test cases 2024-10-07 16:43:13 +03:00
Carlosokumu
2ffe749c9d add driver to group tests 2024-10-07 16:42:48 +03:00
Carlosokumu
0a92444ae8 add group tests 2024-10-07 16:41:45 +03:00
Carlosokumu
2703e6d1df add tags to toggle between online and offline tests 2024-10-05 21:47:17 +03:00
Carlosokumu
86ba03b375 update tests 2024-10-04 17:04:33 +03:00
Carlosokumu
f91480f3de pass account service as a param 2024-10-04 16:58:47 +03:00
Carlosokumu
79a127cea9 pass account service as a parameter 2024-10-04 16:58:18 +03:00
Carlosokumu
6178425355 create a mocked account service 2024-10-04 16:57:25 +03:00
Carlosokumu
09bf266644 pass account service 2024-10-04 16:56:23 +03:00
Carlosokumu
bf1df95af1 pass account service implementation 2024-10-04 16:53:52 +03:00
Carlosokumu
9647d29bd7 resolve failing test 2024-10-04 11:17:19 +03:00
Carlosokumu
64cc9bc752 Merge remote-tracking branch 'refs/remotes/origin/menu-traversals' into menu-traversals 2024-10-04 11:13:58 +03:00
Carlosokumu
ddfd9d4e89 fix failing test 2024-10-04 11:11:41 +03:00
alfred-mk
b7d93c2249 use a UUID for the sessionId 2024-10-03 17:47:48 +03:00
Carlosokumu
a1201e2525 Merge remote-tracking branch 'remotes/origin/master' into menu-traversals 2024-10-03 17:13:40 +03:00
alfred-mk
d6cd2766df add a Random SessionID for each test run 2024-10-03 16:54:30 +03:00
Carlosokumu
e2316b38a8 reset account authorized and perform clean quit on view profile test 2024-10-03 14:55:25 +03:00
alfred-mk
d94037d499 combine the invalid PIN case and remove unused tests 2024-10-03 14:07:36 +03:00
Carlosokumu
0f2ec2f2bd reset authorized flag,update test data file 2024-10-03 13:47:31 +03:00
alfred-mk
089691db4c remove combined test 2024-10-03 12:51:46 +03:00
alfred-mk
1b8c8a12c0 combine the invalid PIN case 2024-10-03 12:51:03 +03:00
alfred-mk
c965d3083d renamed sym to symbol to avoid confusion 2024-10-03 11:53:46 +03:00
alfred-mk
59aa4eae77 removed unused code 2024-10-03 11:45:56 +03:00
alfred-mk
587cfb5a36 use the pp.csv from the scriptDir 2024-10-03 11:45:37 +03:00
alfred-mk
d94758c32c moved the test to test_engine folder 2024-10-03 11:44:34 +03:00
alfred-mk
0fa4a81826 updated the TestAuthorize 2024-10-03 11:42:41 +03:00
alfred-mk
73013f53bb added engine Finish 2024-10-02 15:52:55 +03:00
alfred-mk
986f3979fb use a dynamic public_key 2024-10-02 15:50:52 +03:00
Carlosokumu
96ec3919b2 update test data file 2024-10-02 14:39:48 +03:00
Carlosokumu
079529ebac Merge remote-tracking branch 'refs/remotes/origin/menu-traversals' into menu-traversals 2024-10-02 14:32:08 +03:00
alfred-mk
94bf4ffaa6 combine some test cases and rename the account creation test 2024-10-02 14:28:21 +03:00
alfred-mk
24380e1449 update the templates 2024-10-02 14:25:29 +03:00
Carlosokumu
7f6405d356 Merge remote-tracking branch 'refs/remotes/origin/menu-traversals' into menu-traversals 2024-10-02 13:54:00 +03:00
alfred-mk
2a1e48e5e8 update the test_data with expected output 2024-10-02 13:31:47 +03:00
alfred-mk
6b8f62fbc6 code cleanup - removed unused new lines 2024-10-02 13:26:19 +03:00
alfred-mk
948a9d3a93 allow one to go back during the change PIN 2024-10-02 13:24:03 +03:00
Carlosokumu
01476d5408 update tests 2024-10-02 13:22:00 +03:00
Carlosokumu
ab02a8882a update tests 2024-10-02 12:10:32 +03:00
Carlosokumu
74fa2c3d65 update tests 2024-10-02 11:23:34 +03:00
Carlosokumu
204ce3ce56 update tests 2024-10-02 10:18:04 +03:00
Carlosokumu
abbf22086f update tests 2024-10-02 09:55:56 +03:00
Carlosokumu
6eec9571c7 update test 2024-10-02 09:01:27 +03:00
Carlosokumu
7c0ae80bbf Merge remote-tracking branch 'refs/remotes/origin/menu-traversals' into menu-traversals 2024-10-01 23:23:22 +03:00
Carlosokumu
1ff2c3602f update tests 2024-10-01 23:21:29 +03:00
alfred-mk
a0ef57ca15 add Change PIN test 2024-10-01 21:43:52 +03:00
Carlosokumu
dddb7d7309 merge 2024-10-01 16:54:44 +03:00
Carlosokumu
c5b2348f5e add tests 2024-10-01 16:54:16 +03:00
alfred-mk
48c608ad4d add Help and Quit menu tests 2024-10-01 15:36:59 +03:00
alfred-mk
14218ffb88 add a dynamic value 2024-10-01 14:50:45 +03:00
alfred-mk
3e74c1d939 test with all invalid inputs 2024-10-01 13:26:47 +03:00
alfred-mk
285002738f added send_with_invalid_recipient test 2024-10-01 13:15:05 +03:00
lash
f267aa2b41 Delete connstr in threadgdbm global channel map on close 2024-10-01 00:18:54 +01:00
Carlosokumu
1b2c6933e1 clean up test 2024-09-30 22:27:37 +03:00
Carlosokumu
94d1271dbb update 2024-09-30 22:22:00 +03:00
alfred-mk
f3f276c549 check the status of cont 2024-09-30 22:12:49 +03:00
alfred-mk
9eb35ec52d added en.Finish 2024-09-30 22:00:28 +03:00
alfred-mk
be165e5033 added tests and test cases 2024-09-30 19:55:13 +03:00
Carlosokumu
e7a3de526c remove log 2024-09-30 18:09:53 +03:00
Carlosokumu
cf5b4ec744 call finish on engine 2024-09-30 18:05:20 +03:00
Carlosokumu
80ce141b80 reference correct test data file 2024-09-30 13:31:49 +03:00
Carlosokumu
170d075545 update tests 2024-09-30 13:25:38 +03:00
Carlosokumu
4aad23ab30 update tests 2024-09-30 13:21:17 +03:00
Carlosokumu
7d1db50294 update 2024-09-27 16:26:00 +03:00
Carlosokumu
afe98b8695 setup traversal tests 2024-09-27 15:02:29 +03:00
Carlosokumu
ece80b31f6 start menu traversal tests setup 2024-09-26 21:27:54 +03:00
alfred-mk
221db4e998 return a numbered list of vouchers 2024-09-25 16:06:06 +03:00
alfred-mk
0e376e0d9e include back and quit 2024-09-25 16:03:08 +03:00
Carlosokumu
7aa44caea2 add voucher nodes 2024-09-25 15:57:23 +03:00
Carlosokumu
188cb573dd add dummy vouchers list 2024-09-25 13:27:13 +03:00
70 changed files with 688 additions and 1288 deletions

View File

@@ -1,18 +0,0 @@
#Serve Http
PORT=7123
HOST=127.0.0.1
#PostgreSQL
DB_HOST=localhost
DB_USER=postgres
DB_PASSWORD=strongpass
DB_NAME=urdt_ussd
DB_PORT=5432
DB_SSLMODE=disable
DB_TIMEZONE=Africa/Nairobi
#External API Calls
CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create
TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/
BALANCE_URL=https://custodial.sarafu.africa/api/account/status/
TRACK_URL=http://localhost:5003/api/v2/account/status

View File

@@ -16,8 +16,6 @@ import (
"git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/server"
httpserver "git.grassecon.net/urdt/ussd/internal/http" httpserver "git.grassecon.net/urdt/ussd/internal/http"
@@ -29,10 +27,6 @@ var (
scriptDir = path.Join("services", "registration") scriptDir = path.Join("services", "registration")
) )
func init() {
initializers.LoadEnvVariables()
}
type atRequestParser struct{} type atRequestParser struct{}
func (arp *atRequestParser) GetSessionId(rq any) (string, error) { func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
@@ -72,34 +66,29 @@ func (arp *atRequestParser) GetInput(rq any) ([]byte, error) {
} }
func main() { func main() {
config.LoadConfig()
var dbDir string var dbDir string
var resourceDir string var resourceDir string
var size uint var size uint
var database string
var engineDebug bool var engineDebug bool
var host string var host string
var port uint var port uint
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.StringVar(&database, "db", "gdbm", "database to be used")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output") flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.StringVar(&host, "h", "127.0.0.1", "http host")
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.UintVar(&port, "p", 7123, "http port")
flag.Parse() flag.Parse()
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
ctx := context.Background() ctx := context.Background()
ctx = context.WithValue(ctx, "Database", database)
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{ cfg := engine.Config{
Root: "root", Root: "root",
OutputSize: uint32(size), OutputSize: uint32(size),
FlagCount: uint32(128), FlagCount: uint32(16),
} }
if engineDebug { if engineDebug {

View File

@@ -13,8 +13,6 @@ import (
"git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/server"
"git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/storage"
@@ -25,10 +23,6 @@ var (
scriptDir = path.Join("services", "registration") scriptDir = path.Join("services", "registration")
) )
func init() {
initializers.LoadEnvVariables()
}
type asyncRequestParser struct { type asyncRequestParser struct {
sessionId string sessionId string
input []byte input []byte
@@ -43,36 +37,31 @@ func (p *asyncRequestParser) GetInput(r any) ([]byte, error) {
} }
func main() { func main() {
config.LoadConfig()
var sessionId string var sessionId string
var dbDir string var dbDir string
var resourceDir string var resourceDir string
var size uint var size uint
var database string
var engineDebug bool var engineDebug bool
var host string var host string
var port uint var port uint
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.StringVar(&database, "db", "gdbm", "database to be used")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output") flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.StringVar(&host, "h", "127.0.0.1", "http host")
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.UintVar(&port, "p", 7123, "http port")
flag.Parse() flag.Parse()
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId) logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId)
ctx := context.Background() ctx := context.Background()
ctx = context.WithValue(ctx, "Database", database)
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{ cfg := engine.Config{
Root: "root", Root: "root",
OutputSize: uint32(size), OutputSize: uint32(size),
FlagCount: uint32(128), FlagCount: uint32(16),
} }
if engineDebug { if engineDebug {
@@ -106,6 +95,7 @@ func main() {
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore) lhs.SetDataStore(&userdataStore)
accountService := server.AccountService{} accountService := server.AccountService{}
hl, err := lhs.GetHandler(&accountService) hl, err := lhs.GetHandler(&accountService)

View File

@@ -15,8 +15,6 @@ import (
"git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/server"
httpserver "git.grassecon.net/urdt/ussd/internal/http" httpserver "git.grassecon.net/urdt/ussd/internal/http"
@@ -28,39 +26,30 @@ var (
scriptDir = path.Join("services", "registration") scriptDir = path.Join("services", "registration")
) )
func init() {
initializers.LoadEnvVariables()
}
func main() { func main() {
config.LoadConfig()
var dbDir string var dbDir string
var resourceDir string var resourceDir string
var size uint var size uint
var database string
var engineDebug bool var engineDebug bool
var host string var host string
var port uint var port uint
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.StringVar(&database, "db", "gdbm", "database to be used")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output") flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.StringVar(&host, "h", "127.0.0.1", "http host")
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.UintVar(&port, "p", 7123, "http port")
flag.Parse() flag.Parse()
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
ctx := context.Background() ctx := context.Background()
ctx = context.WithValue(ctx, "Database", database)
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{ cfg := engine.Config{
Root: "root", Root: "root",
OutputSize: uint32(size), OutputSize: uint32(size),
FlagCount: uint32(128), FlagCount: uint32(16),
} }
if engineDebug { if engineDebug {
@@ -99,6 +88,7 @@ func main() {
fmt.Fprintf(os.Stderr, err.Error()) fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1) os.Exit(1)
} }
accountService := server.AccountService{} accountService := server.AccountService{}
hl, err := lhs.GetHandler(&accountService) hl, err := lhs.GetHandler(&accountService)
if err != nil { if err != nil {

View File

@@ -10,8 +10,6 @@ import (
"git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/server"
"git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/storage"
@@ -22,20 +20,12 @@ var (
scriptDir = path.Join("services", "registration") scriptDir = path.Join("services", "registration")
) )
func init() {
initializers.LoadEnvVariables()
}
func main() { func main() {
config.LoadConfig()
var dbDir string var dbDir string
var size uint var size uint
var sessionId string var sessionId string
var database string
var engineDebug bool var engineDebug bool
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") 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.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output") flag.UintVar(&size, "s", 160, "max size of output")
@@ -45,14 +35,13 @@ func main() {
ctx := context.Background() ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "SessionId", sessionId)
ctx = context.WithValue(ctx, "Database", database)
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{ cfg := engine.Config{
Root: "root", Root: "root",
SessionId: sessionId, SessionId: sessionId,
OutputSize: uint32(size), OutputSize: uint32(size),
FlagCount: uint32(128), FlagCount: uint32(16),
} }
resourceDir := scriptDir resourceDir := scriptDir
@@ -96,7 +85,6 @@ func main() {
fmt.Fprintf(os.Stderr, err.Error()) fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1) os.Exit(1)
} }
accountService := server.AccountService{} accountService := server.AccountService{}
hl, err := lhs.GetHandler(&accountService) hl, err := lhs.GetHandler(&accountService)
if err != nil { if err != nil {

View File

@@ -1,18 +1,10 @@
package config package config
import "git.grassecon.net/urdt/ussd/initializers"
var (
CreateAccountURL string const (
TrackStatusURL string CreateAccountURL = "https://custodial.sarafu.africa/api/account/create"
BalanceURL string TrackStatusURL = "https://custodial.sarafu.africa/api/track/"
TrackURL string BalanceURL = "https://custodial.sarafu.africa/api/account/status/"
) )
// LoadConfig initializes the configuration values after environment variables are loaded.
func LoadConfig() {
CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create")
TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/")
BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/")
TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status")
}

View File

@@ -13,12 +13,13 @@ type Step struct {
} }
func (s *Step) MatchesExpectedContent(content []byte) (bool, error) { func (s *Step) MatchesExpectedContent(content []byte) (bool, error) {
pattern := regexp.QuoteMeta(s.ExpectedContent) pattern := `.*\?.*|.*`
re, err := regexp.Compile(pattern) re, err := regexp.Compile(pattern)
if err != nil { if err != nil {
return false, err return false, err
} }
if re.Match([]byte(content)) { // Check if the content matches the regex pattern
if re.Match(content) {
return true, nil return true, nil
} }
return false, nil return false, nil
@@ -37,8 +38,7 @@ type TestCase struct {
} }
func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) { func (s *TestCase) MatchesExpectedContent(content []byte) (bool, error) {
pattern := regexp.QuoteMeta(s.ExpectedContent) re, err := regexp.Compile(s.ExpectedContent)
re, err := regexp.Compile(pattern)
if err != nil { if err != nil {
return false, err return false, err
} }

29
go.mod
View File

@@ -1,48 +1,27 @@
module git.grassecon.net/urdt/ussd module git.grassecon.net/urdt/ussd
go 1.23.0 go 1.22.6
toolchain go1.23.2
require ( require (
git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb
github.com/alecthomas/assert/v2 v2.2.2 github.com/alecthomas/assert/v2 v2.2.2
github.com/peteole/testdata-loader v0.3.0 github.com/peteole/testdata-loader v0.3.0
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 gopkg.in/leonelquinteros/gotext.v1 v1.3.1
)
require github.com/joho/godotenv v1.5.1
require (
github.com/grassrootseconomics/eth-custodial v1.3.0-beta
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
) )
require ( require (
github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/participle/v2 v2.0.0 // indirect
github.com/alecthomas/repr v0.2.0 // indirect github.com/alecthomas/repr v0.2.0 // indirect
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid v4.4.0+incompatible
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

46
go.sum
View File

@@ -1,5 +1,5 @@
git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb h1:6P4kxihcwMjDKzvUFC6t2zGNb7MDW+l/ACGlSAN1N8Y=
git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.defalsify.org/vise.git v0.1.0-rc.3.0.20240923162317-c20d557a3dbb/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= 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/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
@@ -8,65 +8,31 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= 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 h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= 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/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= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk=
github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s=
github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A=
github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I=
github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc= gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc=
gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU= gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,34 +0,0 @@
package initializers
import (
"log"
"os"
"strconv"
"github.com/joho/godotenv"
)
func LoadEnvVariables() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
}
// Helper to get environment variables with a default fallback
func GetEnv(key, defaultVal string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return defaultVal
}
// Helper to safely convert environment variables to uint
func GetEnvUint(key string, defaultVal uint) uint {
if value, exists := os.LookupEnv(key); exists {
if parsed, err := strconv.Atoi(value); err == nil && parsed >= 0 {
return uint(parsed)
}
}
return defaultVal
}

View File

@@ -54,7 +54,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
} }
func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) {
ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, accountService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -83,6 +83,7 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn
ls.DbRs.AddLocalFunc("save_location", ussdHandlers.SaveLocation) ls.DbRs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
ls.DbRs.AddLocalFunc("save_yob", ussdHandlers.SaveYob) ls.DbRs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
ls.DbRs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings) ls.DbRs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
ls.DbRs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
ls.DbRs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized) ls.DbRs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
ls.DbRs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate) ls.DbRs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo) ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
@@ -93,7 +94,6 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_custodial_balances", ussdHandlers.FetchCustodialBalances)
return ussdHandlers, nil return ussdHandlers, nil
} }

View File

@@ -1,37 +1,28 @@
package server package server
import ( import (
"context"
"encoding/json" "encoding/json"
"errors"
"fmt"
"io" "io"
"net/http" "net/http"
"time"
"git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/models"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
)
var (
okResponse api.OKResponse
errResponse api.ErrResponse
) )
type AccountServiceInterface interface { type AccountServiceInterface interface {
CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) CheckBalance(publicKey string) (string, error)
CreateAccount(ctx context.Context) (*api.OKResponse, error) CreateAccount() (*models.AccountResponse, error)
CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) CheckAccountStatus(trackingId string) (string, error)
TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error)
} }
type AccountService struct { type AccountService struct {
} }
type TestAccountService struct { type MockAccountService struct {
} }
// CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID.
//
// Parameters: // Parameters:
// - trackingId: A unique identifier for the account.This should be obtained from a previous call to // - trackingId: A unique identifier for the account.This should be obtained from a previous call to
// CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the // CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the
@@ -40,88 +31,54 @@ type TestAccountService struct {
// Returns: // Returns:
// - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string.
// - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
// If no error occurs, this will be nil // If no error occurs, this will be nil.
func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) {
resp, err := http.Get(config.BalanceURL + trackingId) resp, err := http.Get(config.TrackStatusURL + trackingId)
if err != nil { if err != nil {
return nil, err return "", err
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return "", err
} }
var trackResp models.TrackStatusResponse var trackResp models.TrackStatusResponse
err = json.Unmarshal(body, &trackResp) err = json.Unmarshal(body, &trackResp)
if err != nil { if err != nil {
return nil, err return "", err
}
return &trackResp, nil
}
func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) {
var err error
// Construct the URL with the path parameter
url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
} }
req.Header.Set("Content-Type", "application/json") status := trackResp.Result.Transaction.Status
req.Header.Set("X-GE-KEY", "xd")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
errResponse.Description = err.Error()
return nil, err
}
if resp.StatusCode >= http.StatusBadRequest {
err := json.Unmarshal([]byte(body), &errResponse)
if err != nil {
return nil, err
}
return nil, errors.New(errResponse.Description)
}
err = json.Unmarshal([]byte(body), &okResponse)
if err != nil {
return nil, err
}
if len(okResponse.Result) == 0 {
return nil, errors.New("Empty api result")
}
return &okResponse, nil
return status, nil
} }
// CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
// Parameters: // Parameters:
// - publicKey: The public key associated with the account whose balance needs to be checked. // - publicKey: The public key associated with the account whose balance needs to be checked.
func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { func (as *AccountService) CheckBalance(publicKey string) (string, error) {
resp, err := http.Get(config.BalanceURL + publicKey) resp, err := http.Get(config.BalanceURL + publicKey)
if err != nil { if err != nil {
return nil, err return "0.0", err
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return "0.0", err
} }
var balanceResp models.BalanceResponse var balanceResp models.BalanceResponse
err = json.Unmarshal(body, &balanceResp) err = json.Unmarshal(body, &balanceResp)
if err != nil { if err != nil {
return nil, err return "0.0", err
} }
return &balanceResp, nil
balance := balanceResp.Result.Balance
return balance, nil
} }
// CreateAccount creates a new account in the custodial system. // CreateAccount creates a new account in the custodial system.
@@ -130,55 +87,44 @@ func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*
// If there is an error during the request or processing, this will be nil. // If there is an error during the request or processing, this will be nil.
// - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
// If no error occurs, this will be nil. // If no error occurs, this will be nil.
func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { func (as *AccountService) CreateAccount() (*models.AccountResponse, error) {
var err error resp, err := http.Post(config.CreateAccountURL, "application/json", nil)
// Create a new request
req, err := http.NewRequest("POST", config.CreateAccountURL, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-GE-KEY", "xd")
resp, err := http.DefaultClient.Do(req)
if err != nil {
errResponse.Description = err.Error()
return nil, err
}
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if resp.StatusCode >= http.StatusBadRequest {
err := json.Unmarshal([]byte(body), &errResponse) var accountResp models.AccountResponse
if err != nil { err = json.Unmarshal(body, &accountResp)
return nil, err
}
return nil, errors.New(errResponse.Description)
}
err = json.Unmarshal([]byte(body), &okResponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(okResponse.Result) == 0 {
return nil, errors.New("Empty api result") return &accountResp, nil
}
return &okResponse, nil
} }
func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { func (mas *MockAccountService) CreateAccount() (*models.AccountResponse, error) {
return &api.OKResponse{ return &models.AccountResponse{
Ok: true, Ok: true,
Description: "Account creation request received successfully", Result: struct {
Result: map[string]any{"publicKey": "0x48ADca309b5085852207FAaf2816eD72B52F527C", "trackingId": "28ebe84d-b925-472c-87ae-bbdfa1fb97be"}, 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 }, nil
} }
func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { func (mas *MockAccountService) CheckBalance(publicKey string) (string, error) {
balanceResponse := &models.BalanceResponse{ balanceResponse := &models.BalanceResponse{
Ok: true, Ok: true,
Result: struct { Result: struct {
@@ -189,39 +135,10 @@ func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey strin
Nonce: json.Number("0"), Nonce: json.Number("0"),
}, },
} }
return balanceResponse, nil
return balanceResponse.Result.Balance, nil
} }
func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { func (mas *MockAccountService) CheckAccountStatus(trackingId string) (string, error) {
return &api.OKResponse{ return "SUCCESS", nil
Ok: true,
Description: "Account creation succeeded",
Result: map[string]any{
"active": true,
},
}, nil
}
func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, 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
} }

View File

@@ -10,7 +10,6 @@ import (
"strings" "strings"
"git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/asm"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
"git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/cache"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
@@ -28,8 +27,6 @@ var (
logg = logging.NewVanilla().WithDomain("ussdmenuhandler") logg = logging.NewVanilla().WithDomain("ussdmenuhandler")
scriptDir = path.Join("services", "registration") scriptDir = path.Join("services", "registration")
translationDir = path.Join(scriptDir, "locale") translationDir = path.Join(scriptDir, "locale")
okResponse *api.OKResponse
errResponse *api.ErrResponse
) )
// FlagManager handles centralized flag management // FlagManager handles centralized flag management
@@ -139,18 +136,13 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r
} }
func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error {
flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") accountResp, err := h.accountService.CreateAccount()
okResponse, err := h.accountService.CreateAccount(ctx)
if err != nil {
return err
}
trackingId := okResponse.Result["trackingId"].(string)
publicKey := okResponse.Result["publicKey"].(string)
data := map[utils.DataTyp]string{ data := map[utils.DataTyp]string{
utils.DATA_TRACKING_ID: trackingId, utils.DATA_TRACKING_ID: accountResp.Result.TrackingId,
utils.DATA_PUBLIC_KEY: publicKey, utils.DATA_PUBLIC_KEY: accountResp.Result.PublicKey,
utils.DATA_CUSTODIAL_ID: accountResp.Result.CustodialId.String(),
} }
for key, value := range data { for key, value := range data {
store := h.userdataStore store := h.userdataStore
err := store.WriteEntry(ctx, sessionId, key, []byte(value)) err := store.WriteEntry(ctx, sessionId, key, []byte(value))
@@ -158,8 +150,9 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r
return err return err
} }
} }
flag_account_created, _ := h.flagManager.GetFlag("flag_account_created")
res.FlagSet = append(res.FlagSet, flag_account_created) res.FlagSet = append(res.FlagSet, flag_account_created)
return nil return err
} }
@@ -198,6 +191,7 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou
} }
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
accountPIN := string(input) accountPIN := string(input)
// Validate that the PIN is a 4-digit number // Validate that the PIN is a 4-digit number
if !isValidPIN(accountPIN) { if !isValidPIN(accountPIN) {
@@ -296,6 +290,8 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
//AccountPin, _ := utils.ReadEntry(ctx, h.userdataStore, sessionId, utils.DATA_ACCOUNT_PIN)
store := h.userdataStore store := h.userdataStore
AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN)
if err != nil { if err != nil {
@@ -374,6 +370,7 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
if len(input) == 4 { if len(input) == 4 {
yob := string(input) yob := string(input)
store := h.userdataStore store := h.userdataStore
@@ -416,6 +413,7 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
gender := strings.Split(symbol, "_")[1] gender := strings.Split(symbol, "_")[1]
store := h.userdataStore store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender))
@@ -434,6 +432,7 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
if len(input) > 0 { if len(input) > 0 {
offerings := string(input) offerings := string(input)
store := h.userdataStore store := h.userdataStore
@@ -459,6 +458,7 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt
// ResetAccountAuthorized resets the account authorization flag after a successful PIN entry. // ResetAccountAuthorized resets the account authorization flag after a successful PIN entry.
func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized")
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
@@ -468,10 +468,12 @@ func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input
// CheckIdentifier retrieves the PublicKey from the JSON data file. // CheckIdentifier retrieves the PublicKey from the JSON data file.
func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
store := h.userdataStore store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
@@ -485,10 +487,12 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte
func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
var err error var err error
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized")
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
@@ -498,7 +502,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res
if err != nil { if err != nil {
return res, err return res, err
} }
if len(input) == 4 { if len(input) > 1 {
if bytes.Equal(input, AccountPin) { if bytes.Equal(input, AccountPin) {
if h.st.MatchFlag(flag_account_authorized, false) { if h.st.MatchFlag(flag_account_authorized, false) {
res.FlagReset = append(res.FlagReset, flag_incorrect_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
@@ -521,7 +525,9 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res
// ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt. // ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt.
func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
res.FlagReset = append(res.FlagReset, flag_incorrect_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
return res, nil return res, nil
} }
@@ -533,28 +539,29 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b
flag_account_success, _ := h.flagManager.GetFlag("flag_account_success") flag_account_success, _ := h.flagManager.GetFlag("flag_account_success")
flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending") flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
store := h.userdataStore store := h.userdataStore
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) trackingId, err := store.ReadEntry(ctx, sessionId, utils.DATA_TRACKING_ID)
if err != nil { if err != nil {
return res, err return res, err
} }
okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
status, err := h.accountService.CheckAccountStatus(string(trackingId))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error) fmt.Println("Error checking account status:", err)
return res, err return res, err
} }
res.FlagReset = append(res.FlagReset, flag_api_error)
isActive := okResponse.Result["active"].(bool) err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status))
if !ok { if err != nil {
return res, err return res, nil
} }
if isActive {
if status == "SUCCESS" {
res.FlagSet = append(res.FlagSet, flag_account_success) res.FlagSet = append(res.FlagSet, flag_account_success)
res.FlagReset = append(res.FlagReset, flag_account_pending) res.FlagReset = append(res.FlagReset, flag_account_pending)
} else { } else {
@@ -634,75 +641,23 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (
var res resource.Result var res resource.Result
var err error var err error
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
store := h.userdataStore store := h.userdataStore
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
if err != nil { if err != nil {
return res, err return res, err
} }
balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) balance, err := h.accountService.CheckBalance(string(publicKey))
if err != nil { if err != nil {
return res, nil return res, nil
} }
if !balanceResponse.Ok { res.Content = balance
res.FlagSet = append(res.FlagSet, flag_api_error)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_api_error)
balance := balanceResponse.Result.Balance
res.Content = l.Get("Balance: %s\n", balance)
return res, nil
}
func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
symbol, _ := h.st.Where()
balanceType := strings.Split(symbol, "_")[0]
store := h.userdataStore
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
if err != nil {
return res, err
}
balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey))
if err != nil {
return res, nil
}
if !balanceResponse.Ok {
res.FlagSet = append(res.FlagSet, flag_api_error)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_api_error)
balance := balanceResponse.Result.Balance
switch balanceType {
case "my":
res.Content = fmt.Sprintf("Your balance is %s", balance)
case "community":
res.Content = fmt.Sprintf("Your community balance is %s", balance)
default:
break
}
return res, nil return res, nil
} }
@@ -802,11 +757,10 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res
store := h.userdataStore store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
balanceResp, err := h.accountService.CheckBalance(ctx, string(publicKey)) balance, err := h.accountService.CheckBalance(string(publicKey))
if err != nil { if err != nil {
return res, nil return res, nil
} }
balance := balanceResp.Result.Balance
res.Content = balance res.Content = balance
@@ -825,25 +779,18 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte)
} }
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
store := h.userdataStore store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
amountStr := string(input) amountStr := string(input)
balanceRes, err := h.accountService.CheckBalance(ctx, string(publicKey)) balanceStr, err := h.accountService.CheckBalance(string(publicKey))
balanceStr := balanceRes.Result.Balance
if !balanceRes.Ok {
res.FlagSet = append(res.FlagSet, flag_api_error)
return res, nil
}
if err != nil { if err != nil {
return res, err return res, err
} }
res.Content = balanceStr res.Content = balanceStr
res.FlagReset = append(res.FlagReset, flag_api_error)
// Parse the balance // Parse the balance
balanceParts := strings.Split(balanceStr, " ") balanceParts := strings.Split(balanceStr, " ")
@@ -902,7 +849,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (
return res, nil return res, nil
} }
// GetSender returns the sessionId (phoneNumber) // GetSender retrieves the public key from the Gdbm Db
func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
@@ -911,7 +858,10 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
res.Content = string(sessionId) store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
res.Content = string(publicKey)
return res, nil return res, nil
} }
@@ -932,6 +882,36 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res
return res, nil return res, nil
} }
// QuickWithBalance retrieves the balance for a given public key from the custodial balance API endpoint before
// gracefully exiting the session.
func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
store := h.userdataStore
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
if err != nil {
return res, err
}
balance, err := h.accountService.CheckBalance(string(publicKey))
if err != nil {
return res, nil
}
res.Content = l.Get("Your account balance is %s", balance)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}
// InitiateTransaction returns a confirmation and resets the transaction data // InitiateTransaction returns a confirmation and resets the transaction data
// on the gdbm store. // on the gdbm store.
func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []byte) (resource.Result, error) {
@@ -948,12 +928,13 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []
// TODO // TODO
// Use the amount, recipient and sender to call the API and initialize the transaction // Use the amount, recipient and sender to call the API and initialize the transaction
store := h.userdataStore store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT)
recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT)
res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(sessionId)) res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey))
account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized")
if err != nil { if err != nil {
@@ -964,23 +945,16 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []
return res, nil return res, nil
} }
// GetProfileInfo retrieves and formats the profile information of a user from a Gdbm backed storage.
func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
var defaultValue string
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
language, ok := ctx.Value("Language").(lang.Language)
if !ok { // Default value when an entry is not found
return res, fmt.Errorf("value for 'Language' is not of type lang.Language") defaultValue := "Not Provided"
}
code := language.Code
if code == "swa" {
defaultValue = "Haipo"
} else {
defaultValue = "Not Provided"
}
// Helper function to handle nil byte slices and convert them to string // Helper function to handle nil byte slices and convert them to string
getEntryOrDefault := func(entry []byte, err error) string { getEntryOrDefault := func(entry []byte, err error) string {
@@ -1017,23 +991,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte)
return res, fmt.Errorf("invalid year of birth: %v", err) return res, fmt.Errorf("invalid year of birth: %v", err)
} }
} }
switch language.Code {
case "eng": // Format the result
res.Content = fmt.Sprintf( res.Content = fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
name, gender, age, location, offerings, name, gender, age, location, offerings,
) )
case "swa":
res.Content = fmt.Sprintf(
"Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n",
name, gender, age, location, offerings,
)
default:
res.Content = fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
name, gender, age, location, offerings,
)
}
return res, nil return res, nil
} }

View File

@@ -7,20 +7,15 @@ import (
"log" "log"
"path" "path"
"testing" "testing"
"time"
"git.defalsify.org/vise.git/asm"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/lang"
"git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state" "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/mocks"
"git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/models"
"git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/internal/utils"
"github.com/alecthomas/assert/v2" "github.com/alecthomas/assert/v2"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
testdataloader "github.com/peteole/testdata-loader" testdataloader "github.com/peteole/testdata-loader"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -30,117 +25,75 @@ var (
flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") flagsPath = path.Join(baseDir, "services", "registration", "pp.csv")
) )
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, &accountService)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if handlers == nil {
t.Fatal("expected handlers to be non-nil")
}
if handlers.userdataStore == nil {
t.Fatal("expected userdataStore to be set in handlers")
}
})
// Test case for nil userdataStore
t.Run("Nil UserDataStore", func(t *testing.T) {
appFlags := &asm.FlagParser{}
handlers, err := NewHandlers(appFlags, nil, &accountService)
if err == nil {
t.Fatal("expected an error, got none")
}
if handlers != nil {
t.Fatal("expected handlers to be nil")
}
if err.Error() != "cannot create handler with nil userdata store" {
t.Fatalf("expected specific error, got %v", err)
}
})
}
func TestCreateAccount(t *testing.T) { func TestCreateAccount(t *testing.T) {
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
// Create required mocks // Create required mocks
flag_account_created, err := fm.GetFlag("flag_account_created") mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService)
expectedResult := resource.Result{}
accountCreatedFlag, err := fm.GetFlag("flag_account_created")
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
expectedResult.FlagSet = append(expectedResult.FlagSet, accountCreatedFlag)
// Define session ID and mock data // Define session ID and mock data
sessionId := "session123" sessionId := "session123"
notFoundErr := db.ErrNotFound{} typ := utils.DATA_ACCOUNT_CREATED
fakeError := db.ErrNotFound{}
// Create context with session ID
ctx := context.WithValue(context.Background(), "SessionId", sessionId) ctx := context.WithValue(context.Background(), "SessionId", sessionId)
tests := []struct { // Define expected interactions with the mock
name string mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte("123"), fakeError)
serverResponse *api.OKResponse expectedAccountResp := &models.AccountResponse{
expectedResult resource.Result Ok: true,
}{ Result: struct {
{ CustodialId json.Number `json:"custodialId"`
name: "Test account creation success", PublicKey string `json:"publicKey"`
serverResponse: &api.OKResponse{ TrackingId string `json:"trackingId"`
Ok: true, }{
Description: "Account creation successed", CustodialId: "12",
Result: map[string]any{ PublicKey: "0x8E0XSCSVA",
"trackingId": "1234567890", TrackingId: "d95a7e83-196c-4fd0-866fSGAGA",
"publicKey": "1235QERYU",
},
},
expectedResult: resource.Result{
FlagSet: []uint32{flag_account_created},
},
}, },
} }
for _, tt := range tests { mockCreateAccountService.On("CreateAccount").Return(expectedAccountResp, nil)
t.Run(tt.name, func(t *testing.T) { data := map[utils.DataTyp]string{
utils.DATA_TRACKING_ID: expectedAccountResp.Result.TrackingId,
mockDataStore := new(mocks.MockUserDataStore) utils.DATA_PUBLIC_KEY: expectedAccountResp.Result.PublicKey,
mockCreateAccountService := new(mocks.MockAccountService) utils.DATA_CUSTODIAL_ID: expectedAccountResp.Result.CustodialId.String(),
// Create a Handlers instance with the mock data store
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
flagManager: fm.parser,
}
data := map[utils.DataTyp]string{
utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string),
utils.DATA_PUBLIC_KEY: tt.serverResponse.Result["publicKey"].(string),
}
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr)
mockCreateAccountService.On("CreateAccount").Return(tt.serverResponse, nil)
for key, value := range data {
mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil)
}
// Call the method you want to test
res, err := h.CreateAccount(ctx, "create_account", []byte("some-input"))
// Assert that no errors occurred
assert.NoError(t, err)
// Assert that the account created flag has been set to the result
assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result")
// Assert that expectations were met
mockDataStore.AssertExpectations(t)
})
} }
for key, value := range data {
mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil)
}
// Create a Handlers instance with the mock data store
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
flagManager: fm.parser,
}
// Call the method you want to test
res, err := h.CreateAccount(ctx, "create_account", []byte("some-input"))
// Assert that no errors occurred
assert.NoError(t, err)
//Assert that the account created flag has been set to the result
assert.Equal(t, res, expectedResult, "Expected result should be equal to the actual result")
// Assert that expectations were met
mockDataStore.AssertExpectations(t)
} }
func TestWithPersister(t *testing.T) { func TestWithPersister(t *testing.T) {
@@ -489,10 +442,7 @@ func TestMaxAmount(t *testing.T) {
sessionId := "session123" sessionId := "session123"
ctx := context.WithValue(context.Background(), "SessionId", sessionId) ctx := context.WithValue(context.Background(), "SessionId", sessionId)
publicKey := "0xcasgatweksalw1018221" publicKey := "0xcasgatweksalw1018221"
expectedBalance := "0.003CELO"
expectedBalance := &models.BalanceResponse{
Ok: true,
}
// Set up the expected behavior of the mock // Set up the expected behavior of the mock
mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
@@ -508,7 +458,7 @@ func TestMaxAmount(t *testing.T) {
res, _ := h.MaxAmount(ctx, "max_amount", []byte("check_balance")) res, _ := h.MaxAmount(ctx, "max_amount", []byte("check_balance"))
//Assert that the balance that was set as the result content is what was returned by Check Balance //Assert that the balance that was set as the result content is what was returned by Check Balance
assert.Equal(t, expectedBalance.Result.Balance, res.Content) assert.Equal(t, expectedBalance, res.Content)
} }
@@ -516,8 +466,12 @@ func TestGetSender(t *testing.T) {
mockStore := new(mocks.MockUserDataStore) mockStore := new(mocks.MockUserDataStore)
// Define test data // Define test data
sessionId := "254712345678" sessionId := "session123"
ctx := context.WithValue(context.Background(), "SessionId", sessionId) ctx := context.WithValue(context.Background(), "SessionId", sessionId)
publicKey := "0xcasgatweksalw1018221"
// Set up the expected behavior of the mock
mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
// Create the Handlers instance with the mock store // Create the Handlers instance with the mock store
h := &Handlers{ h := &Handlers{
@@ -525,10 +479,11 @@ func TestGetSender(t *testing.T) {
} }
// Call the method // Call the method
res, _ := h.GetSender(ctx, "get_sender", []byte("")) res, _ := h.GetSender(ctx, "max_amount", []byte("check_balance"))
//Assert that the public key from readentry operation is what was set as the result content.
assert.Equal(t, publicKey, res.Content)
//Assert that the sessionId is what was set as the result content.
assert.Equal(t, sessionId, res.Content)
} }
func TestGetAmount(t *testing.T) { func TestGetAmount(t *testing.T) {
@@ -582,10 +537,12 @@ func TestGetRecipient(t *testing.T) {
func TestGetFlag(t *testing.T) { func TestGetFlag(t *testing.T) {
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
expectedFlag := uint32(9) expectedFlag := uint32(9)
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
flag, err := fm.GetFlag("flag_account_created") flag, err := fm.GetFlag("flag_account_created")
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
@@ -879,7 +836,10 @@ func TestAuthorize(t *testing.T) {
{ {
name: "Test with pin that is not a 4 digit", name: "Test with pin that is not a 4 digit",
input: []byte("1235aqds"), input: []byte("1235aqds"),
expectedResult: resource.Result{}, expectedResult: resource.Result{
FlagReset: []uint32{flag_account_authorized},
FlagSet: []uint32{flag_incorrect_pin},
},
}, },
} }
@@ -1055,115 +1015,53 @@ func TestVerifyPin(t *testing.T) {
func TestCheckAccountStatus(t *testing.T) { func TestCheckAccountStatus(t *testing.T) {
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService)
sessionId := "session123" sessionId := "session123"
flag_account_success, _ := fm.GetFlag("flag_account_success") flag_account_success, _ := fm.GetFlag("flag_account_success")
flag_account_pending, _ := fm.GetFlag("flag_account_pending") flag_account_pending, _ := fm.GetFlag("flag_account_pending")
flag_api_error, _ := fm.GetFlag("flag_api_call_error")
ctx := context.WithValue(context.Background(), "SessionId", sessionId) ctx := context.WithValue(context.Background(), "SessionId", sessionId)
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
flagManager: fm.parser,
}
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
serverResponse *api.OKResponse status string
response *models.TrackStatusResponse
expectedResult resource.Result expectedResult resource.Result
}{ }{
{ {
name: "Test when account is on the Sarafu network", name: "Test when account status is Success",
input: []byte("TrackingId1234"), input: []byte("TrackingId1234"),
serverResponse: &api.OKResponse{ status: "SUCCESS",
Ok: true,
Description: "Account creation succeeded",
Result: map[string]any{
"active": true,
},
},
response: &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",
},
},
},
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagSet: []uint32{flag_account_success}, FlagSet: []uint32{flag_account_success},
FlagReset: []uint32{flag_api_error, flag_account_pending}, FlagReset: []uint32{flag_account_pending},
},
},
{
name: "Test when the account is not yet on the sarafu network",
input: []byte("TrackingId1234"),
response: &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",
},
},
},
serverResponse: &api.OKResponse{
Ok: true,
Description: "Account creation succeeded",
Result: map[string]any{
"active": false,
},
},
expectedResult: resource.Result{
FlagSet: []uint32{flag_account_pending},
FlagReset: []uint32{flag_api_error, flag_account_success},
}, },
}, },
} }
typ := utils.DATA_TRACKING_ID
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService)
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
flagManager: fm.parser,
}
status := tt.response.Result.Transaction.Status
// Define expected interactions with the mock // Define expected interactions with the mock
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.input, nil) mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return(tt.input, nil)
mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.status, nil)
mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(tt.status)).Return(nil)
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe()
// Call the method under test // Call the method under test
res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) res, _ := h.CheckAccountStatus(ctx, "check_status", tt.input)
// Assert that no errors occurred // Assert that no errors occurred
assert.NoError(t, err) assert.NoError(t, err)
@@ -1292,7 +1190,7 @@ func TestResetInvalidAmount(t *testing.T) {
} }
func TestInitiateTransaction(t *testing.T) { func TestInitiateTransaction(t *testing.T) {
sessionId := "254712345678" sessionId := "session123"
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
@@ -1315,26 +1213,30 @@ func TestInitiateTransaction(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
PublicKey []byte
Recipient []byte Recipient []byte
Amount []byte Amount []byte
status string status string
expectedResult resource.Result expectedResult resource.Result
}{ }{
{ {
name: "Test initiate transaction", name: "Test amount reset",
Amount: []byte("0.002 CELO"), PublicKey: []byte("0x1241527192"),
Amount: []byte("0.002CELO"),
Recipient: []byte("0x12415ass27192"), Recipient: []byte("0x12415ass27192"),
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagReset: []uint32{account_authorized_flag}, FlagReset: []uint32{account_authorized_flag},
Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 CELO from 254712345678.", Content: "Your request has been sent. 0x12415ass27192 will receive 0.002CELO from 0x1241527192.",
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Define expected interactions with the mock // Define expected interactions with the mock
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.PublicKey, nil)
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil)
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil)
//mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil)
// Call the method under test // Call the method under test
res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input)
@@ -1347,8 +1249,10 @@ func TestInitiateTransaction(t *testing.T) {
// Assert that expectations were met // Assert that expectations were met
mockDataStore.AssertExpectations(t) mockDataStore.AssertExpectations(t)
}) })
} }
} }
func TestQuit(t *testing.T) { func TestQuit(t *testing.T) {
@@ -1457,14 +1361,14 @@ func TestIsValidPIN(t *testing.T) {
} }
} }
func TestValidateAmount(t *testing.T) { func TestQuitWithBalance(t *testing.T) {
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") flag_account_authorized, _ := fm.parser.GetFlag("flag_account_authorized")
flag_api_error, _ := fm.GetFlag("flag_api_call_error")
mockDataStore := new(mocks.MockUserDataStore) mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService) mockCreateAccountService := new(mocks.MockAccountService)
@@ -1478,69 +1382,19 @@ func TestValidateAmount(t *testing.T) {
flagManager: fm.parser, flagManager: fm.parser,
} }
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
publicKey []byte publicKey []byte
balanceResponse *models.BalanceResponse balance string
expectedResult resource.Result expectedResult resource.Result
}{ }{
{ {
name: "Test with valid amount", name: "Test quit with balance",
input: []byte("0.001"), balance: "0.02CELO",
balanceResponse: &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
publicKey: []byte("0xrqeqrequuq"), publicKey: []byte("0xrqeqrequuq"),
expectedResult: resource.Result{ expectedResult: resource.Result{
Content: "0.001", FlagReset: []uint32{flag_account_authorized},
FlagReset: []uint32{flag_api_error}, Content: fmt.Sprintf("Your account balance is %s", "0.02CELO"),
},
},
{
name: "Test with amount larger than balance",
input: []byte("0.02"),
balanceResponse: &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
publicKey: []byte("0xrqeqrequuq"),
expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_amount},
FlagReset: []uint32{flag_api_error},
Content: "0.02",
},
},
{
name: "Test with invalid amount",
input: []byte("0.02ms"),
balanceResponse: &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
publicKey: []byte("0xrqeqrequuq"),
expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_amount},
FlagReset: []uint32{flag_api_error},
Content: "0.02ms",
}, },
}, },
} }
@@ -1549,7 +1403,86 @@ func TestValidateAmount(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil)
mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balanceResponse, nil) mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil)
// Call the method under test
res, _ := h.QuitWithBalance(ctx, "test_quit_with_balance", tt.input)
// Assert that no errors occurred
assert.NoError(t, err)
//Assert that the account created flag has been set to the result
assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result")
// Assert that expectations were met
mockDataStore.AssertExpectations(t)
})
}
}
func TestValidateAmount(t *testing.T) {
fm, err := NewFlagManager(flagsPath)
if err != nil {
t.Logf(err.Error())
}
flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount")
mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService)
sessionId := "session123"
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
h := &Handlers{
userdataStore: mockDataStore,
accountService: mockCreateAccountService,
flagManager: fm.parser,
}
tests := []struct {
name string
input []byte
publicKey []byte
balance string
expectedResult resource.Result
}{
{
name: "Test with valid amount",
input: []byte("0.001"),
balance: "0.003 CELO",
publicKey: []byte("0xrqeqrequuq"),
expectedResult: resource.Result{
Content: "0.001",
},
},
{
name: "Test with amount larger than balance",
input: []byte("0.02"),
balance: "0.003 CELO",
publicKey: []byte("0xrqeqrequuq"),
expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_amount},
Content: "0.02",
},
},
{
name: "Test with invalid amount",
input: []byte("0.02ms"),
balance: "0.003 CELO",
publicKey: []byte("0xrqeqrequuq"),
expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_amount},
Content: "0.02ms",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.publicKey, nil)
mockCreateAccountService.On("CheckBalance", string(tt.publicKey)).Return(tt.balance, nil)
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe() mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe()
// Call the method under test // Call the method under test
@@ -1627,82 +1560,33 @@ func TestValidateRecipient(t *testing.T) {
} }
func TestCheckBalance(t *testing.T) { func TestCheckBalance(t *testing.T) {
mockDataStore := new(mocks.MockUserDataStore)
sessionId := "session123" sessionId := "session123"
publicKey := "0X13242618721" publicKey := "0X13242618721"
fm, _ := NewFlagManager(flagsPath) balance := "0.003 CELO"
flag_api_error, _ := fm.GetFlag("flag_api_call_error")
expectedResult := resource.Result{
Content: "0.003 CELO",
}
mockCreateAccountService := new(mocks.MockAccountService)
ctx := context.WithValue(context.Background(), "SessionId", sessionId) ctx := context.WithValue(context.Background(), "SessionId", sessionId)
tests := []struct { h := &Handlers{
name string userdataStore: mockDataStore,
balanceResonse *models.BalanceResponse accountService: mockCreateAccountService,
expectedResult resource.Result //flagManager: fm.parser,
}{
{
name: "Test when checking a balance is not a success",
balanceResonse: &models.BalanceResponse{
Ok: false,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
expectedResult: resource.Result{
FlagSet: []uint32{flag_api_error},
},
},
{
name: "Test when checking a balance is a success",
balanceResonse: &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
expectedResult: resource.Result{
Content: "Balance: 0.003 CELO\n",
FlagReset: []uint32{flag_api_error},
},
},
} }
//mock call operations
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(balance, nil)
for _, tt := range tests { res, _ := h.CheckBalance(ctx, "check_balance", []byte("123456"))
t.Run(tt.name, func(t *testing.T) {
mockDataStore := new(mocks.MockUserDataStore) assert.Equal(t, res, expectedResult, "Result should contain flag(s) that have been reset")
mockCreateAccountService := new(mocks.MockAccountService)
mockState := state.NewState(16)
// Create the Handlers instance with the mock store
h := &Handlers{
userdataStore: mockDataStore,
flagManager: fm.parser,
st: mockState,
accountService: mockCreateAccountService,
}
// Set up the expected behavior of the mock
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil)
// Call the method
res, _ := h.CheckBalance(ctx, "check_balance", []byte(""))
// Assert that expectations were met
mockDataStore.AssertExpectations(t)
//Assert that the result set to content is what was expected
assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input")
})
}
} }
func TestGetProfile(t *testing.T) { func TestGetProfile(t *testing.T) {
@@ -1711,50 +1595,23 @@ func TestGetProfile(t *testing.T) {
mockDataStore := new(mocks.MockUserDataStore) mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService) mockCreateAccountService := new(mocks.MockAccountService)
mockState := state.NewState(16)
h := &Handlers{ h := &Handlers{
userdataStore: mockDataStore, userdataStore: mockDataStore,
accountService: mockCreateAccountService, accountService: mockCreateAccountService,
st: mockState,
} }
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
tests := []struct { tests := []struct {
name string name string
languageCode string keys []utils.DataTyp
keys []utils.DataTyp profileInfo []string
profileInfo []string result resource.Result
result resource.Result
}{ }{
{ {
name: "Test with full profile information in eng", name: "Test with full profile information",
keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB},
profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"},
languageCode: "eng",
result: resource.Result{
Content: fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
"John Doee", "Male", "48", "Kilifi", "Bananas",
),
},
},
{
name: "Test with with profile information in swa ",
keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB},
profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"},
languageCode: "swa",
result: resource.Result{
Content: fmt.Sprintf(
"Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n",
"John Doee", "Male", "48", "Kilifi", "Bananas",
),
},
},
{
name: "Test with with profile information with language that is not yet supported",
keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB},
profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"},
languageCode: "nor",
result: resource.Result{ result: resource.Result{
Content: fmt.Sprintf( Content: fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
@@ -1765,14 +1622,9 @@ func TestGetProfile(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
ctx = context.WithValue(ctx, "Language", lang.Language{
Code: tt.languageCode,
})
for index, key := range tt.keys { for index, key := range tt.keys {
mockDataStore.On("ReadEntry", ctx, sessionId, key).Return([]byte(tt.profileInfo[index]), nil).Maybe() mockDataStore.On("ReadEntry", ctx, sessionId, key).Return([]byte(tt.profileInfo[index]), nil)
} }
res, _ := h.GetProfileInfo(ctx, "get_profile_info", []byte("")) res, _ := h.GetProfileInfo(ctx, "get_profile_info", []byte(""))
// Assert that expectations were met // Assert that expectations were met
@@ -1929,85 +1781,3 @@ func TestConfirmPin(t *testing.T) {
} }
} }
func TestFetchCustodialBalances(t *testing.T) {
fm, err := NewFlagManager(flagsPath)
if err != nil {
t.Logf(err.Error())
}
flag_api_error, _ := fm.GetFlag("flag_api_call_error")
// Define test data
sessionId := "session123"
publicKey := "0X13242618721"
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
tests := []struct {
name string
balanceResonse *models.BalanceResponse
expectedResult resource.Result
}{
{
name: "Test when fetch custodial balances is not a success",
balanceResonse: &models.BalanceResponse{
Ok: false,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
expectedResult: resource.Result{
FlagSet: []uint32{flag_api_error},
},
},
{
name: "Test when fetch custodial balances is a success",
balanceResonse: &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
},
expectedResult: resource.Result{
FlagReset: []uint32{flag_api_error},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDataStore := new(mocks.MockUserDataStore)
mockCreateAccountService := new(mocks.MockAccountService)
mockState := state.NewState(16)
// Create the Handlers instance with the mock store
h := &Handlers{
userdataStore: mockDataStore,
flagManager: fm.parser,
st: mockState,
accountService: mockCreateAccountService,
}
// Set up the expected behavior of the mock
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil)
// Call the method
res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte(""))
// Assert that expectations were met
mockDataStore.AssertExpectations(t)
//Assert that the result set to content is what was expected
assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input")
})
}
}

View File

@@ -1,10 +1,7 @@
package mocks package mocks
import ( import (
"context"
"git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/models"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )
@@ -13,22 +10,17 @@ type MockAccountService struct {
mock.Mock mock.Mock
} }
func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) {
args := m.Called() args := m.Called()
return args.Get(0).(*api.OKResponse), args.Error(1) return args.Get(0).(*models.AccountResponse), args.Error(1)
} }
func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { func (m *MockAccountService) CheckBalance(publicKey string) (string, error) {
args := m.Called(publicKey) args := m.Called(publicKey)
return args.Get(0).(*models.BalanceResponse), args.Error(1) return args.String(0), args.Error(1)
} }
func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, error) {
args := m.Called(trackingId) args := m.Called(trackingId)
return args.Get(0).(*models.TrackStatusResponse), args.Error(1) return args.String(0), args.Error(1)
} }
func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey string) (*api.OKResponse, error) {
args := m.Called(publicKey)
return args.Get(0).(*api.OKResponse), args.Error(1)
}

View File

@@ -1,10 +1,15 @@
package models package models
import (
"encoding/json"
)
type AccountResponse struct { type AccountResponse struct {
Ok bool `json:"ok"` Ok bool `json:"ok"`
Description string `json:"description"` // Include the description field Result struct {
Result struct { CustodialId json.Number `json:"custodialId"`
PublicKey string `json:"publicKey"` PublicKey string `json:"publicKey"`
TrackingId string `json:"trackingId"` TrackingId string `json:"trackingId"`
} `json:"result"` } `json:"result"`
} }

View File

@@ -4,13 +4,7 @@ import (
"encoding/json" "encoding/json"
"time" "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 { type TrackStatusResponse struct {
Ok bool `json:"ok"` Ok bool `json:"ok"`
@@ -23,4 +17,4 @@ type TrackStatusResponse struct {
TxType string `json:"txType"` TxType string `json:"txType"`
} }
} `json:"result"` } `json:"result"`
} }

View File

@@ -1,43 +0,0 @@
package storage
import (
"context"
"git.defalsify.org/vise.git/db"
)
const (
DATATYPE_USERSUB = 64
)
type SubPrefixDb struct {
store db.Db
pfx []byte
}
func NewSubPrefixDb(store db.Db, pfx []byte) *SubPrefixDb {
return &SubPrefixDb{
store: store,
pfx: pfx,
}
}
func(s *SubPrefixDb) toKey(k []byte) []byte {
return append(s.pfx, k...)
}
func(s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) {
s.store.SetPrefix(DATATYPE_USERSUB)
key = s.toKey(key)
v, err := s.store.Get(ctx, key)
if err != nil {
return nil, err
}
return v, nil
}
func(s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error {
s.store.SetPrefix(DATATYPE_USERSUB)
key = s.toKey(key)
return s.store.Put(ctx, key, val)
}

View File

@@ -1,54 +0,0 @@
package storage
import (
"bytes"
"context"
"testing"
memdb "git.defalsify.org/vise.git/db/mem"
)
func TestSubPrefix(t *testing.T) {
ctx := context.Background()
db := memdb.NewMemDb()
err := db.Connect(ctx, "")
if err != nil {
t.Fatal(err)
}
sdba := NewSubPrefixDb(db, []byte("tinkywinky"))
err = sdba.Put(ctx, []byte("foo"), []byte("dipsy"))
if err != nil {
t.Fatal(err)
}
r, err := sdba.Get(ctx, []byte("foo"))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(r, []byte("dipsy")) {
t.Fatalf("expected 'dipsy', got %s", r)
}
sdbb := NewSubPrefixDb(db, []byte("lala"))
r, err = sdbb.Get(ctx, []byte("foo"))
if err == nil {
t.Fatal("expected not found")
}
err = sdbb.Put(ctx, []byte("foo"), []byte("pu"))
if err != nil {
t.Fatal(err)
}
r, err = sdbb.Get(ctx, []byte("foo"))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(r, []byte("pu")) {
t.Fatalf("expected 'pu', got %s", r)
}
r, err = sdba.Get(ctx, []byte("foo"))
if !bytes.Equal(r, []byte("dipsy")) {
t.Fatalf("expected 'dipsy', got %s", r)
}
}

View File

@@ -5,6 +5,10 @@ import (
"git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/persist"
) )
const (
DATATYPE_CUSTOM = 128
)
type Storage struct { type Storage struct {
Persister *persist.Persister Persister *persist.Persister
UserdataDb db.Db UserdataDb db.Db

View File

@@ -8,16 +8,14 @@ import (
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
fsdb "git.defalsify.org/vise.git/db/fs" fsdb "git.defalsify.org/vise.git/db/fs"
"git.defalsify.org/vise.git/db/postgres"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/initializers" "git.defalsify.org/vise.git/logging"
) )
var ( var (
logg = logging.NewVanilla().WithDomain("storage") logg = logging.NewVanilla().WithDomain("storage")
) )
type StorageService interface { type StorageService interface {
GetPersister(ctx context.Context) (*persist.Persister, error) GetPersister(ctx context.Context) (*persist.Persister, error)
@@ -26,86 +24,40 @@ type StorageService interface {
EnsureDbDir() error EnsureDbDir() error
} }
type MenuStorageService struct { type MenuStorageService struct{
dbDir string dbDir string
resourceDir string resourceDir string
resourceStore db.Db resourceStore db.Db
stateStore db.Db stateStore db.Db
userDataStore db.Db userDataStore db.Db
} }
func buildConnStr() string {
host := initializers.GetEnv("DB_HOST", "localhost")
user := initializers.GetEnv("DB_USER", "postgres")
password := initializers.GetEnv("DB_PASSWORD", "")
dbName := initializers.GetEnv("DB_NAME", "")
port := initializers.GetEnv("DB_PORT", "5432")
return fmt.Sprintf(
"postgres://%s:%s@%s:%s/%s",
user, password, host, port, dbName,
)
}
func NewMenuStorageService(dbDir string, resourceDir string) *MenuStorageService { func NewMenuStorageService(dbDir string, resourceDir string) *MenuStorageService {
return &MenuStorageService{ return &MenuStorageService{
dbDir: dbDir, dbDir: dbDir,
resourceDir: resourceDir, resourceDir: resourceDir,
} }
} }
func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.Db, fileName string) (db.Db, error) {
database, ok := ctx.Value("Database").(string)
if !ok {
return nil, fmt.Errorf("failed to select the database")
}
if existingDb != nil {
return existingDb, nil
}
var newDb db.Db
var err error
if database == "postgres" {
newDb = postgres.NewPgDb()
connStr := buildConnStr()
err = newDb.Connect(ctx, connStr)
} else {
newDb = NewThreadGdbmDb()
storeFile := path.Join(ms.dbDir, fileName)
err = newDb.Connect(ctx, storeFile)
}
if err != nil {
return nil, err
}
return newDb, nil
}
func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) {
stateStore, err := ms.GetStateStore(ctx) ms.stateStore = NewThreadGdbmDb()
storeFile := path.Join(ms.dbDir, "state.gdbm")
err := ms.stateStore.Connect(ctx, storeFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
pr := persist.NewPersister(ms.stateStore)
pr := persist.NewPersister(stateStore) logg.TraceCtxf(ctx, "menu storage service", "persist", pr, "store", ms.stateStore)
logg.TraceCtxf(ctx, "menu storage service", "persist", pr, "store", stateStore)
return pr, nil return pr, nil
} }
func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { func (ms *MenuStorageService) GetUserdataDb(ctx context.Context) (db.Db, error) {
if ms.userDataStore != nil { ms.userDataStore = NewThreadGdbmDb()
return ms.userDataStore, nil storeFile := path.Join(ms.dbDir, "userdata.gdbm")
} err := ms.userDataStore.Connect(ctx, storeFile)
userDataStore, err := ms.getOrCreateDb(ctx, ms.userDataStore, "userdata.gdbm")
if err != nil { if err != nil {
return nil, err return nil, err
} }
ms.userDataStore = userDataStore
return ms.userDataStore, nil return ms.userDataStore, nil
} }
@@ -121,15 +73,14 @@ func (ms *MenuStorageService) GetResource(ctx context.Context) (resource.Resourc
func (ms *MenuStorageService) GetStateStore(ctx context.Context) (db.Db, error) { func (ms *MenuStorageService) GetStateStore(ctx context.Context) (db.Db, error) {
if ms.stateStore != nil { if ms.stateStore != nil {
return ms.stateStore, nil panic("set up store when already exists")
} }
ms.stateStore = NewThreadGdbmDb()
stateStore, err := ms.getOrCreateDb(ctx, ms.stateStore, "state.gdbm") storeFile := path.Join(ms.dbDir, "state.gdbm")
err := ms.stateStore.Connect(ctx, storeFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ms.stateStore = stateStore
return ms.stateStore, nil return ms.stateStore, nil
} }

View File

@@ -25,7 +25,6 @@ var (
func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
ctx := context.Background() ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "SessionId", sessionId)
ctx = context.WithValue(ctx, "Database", "gdbm")
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
var eventChannel = make(chan bool) var eventChannel = make(chan bool)
@@ -34,7 +33,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
Root: "root", Root: "root",
SessionId: sessionId, SessionId: sessionId,
OutputSize: uint32(160), OutputSize: uint32(160),
FlagCount: uint32(128), FlagCount: uint32(16),
} }
dbDir := ".test_state" dbDir := ".test_state"
@@ -80,12 +79,8 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
os.Exit(1) os.Exit(1)
} }
if AccountService == nil {
AccountService = &server.AccountService{}
}
switch AccountService.(type) { switch AccountService.(type) {
case *server.TestAccountService: case *server.MockAccountService:
go func() { go func() {
eventChannel <- false eventChannel <- false
}() }()
@@ -118,5 +113,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
} }
logg.Infof("testengine storage closed") logg.Infof("testengine storage closed")
} }
//en = en.WithDebug(nil)
return en, cleanFn, eventChannel return en, cleanFn, eventChannel
} }

View File

@@ -1,11 +0,0 @@
// +build !online
package testutil
import (
"git.grassecon.net/urdt/ussd/internal/handlers/server"
)
var (
AccountService server.AccountServiceInterface = &server.TestAccountService{}
)

View File

@@ -1,9 +0,0 @@
// +build online
package testutil
import "git.grassecon.net/urdt/ussd/internal/handlers/server"
var (
AccountService server.AccountServiceInterface
)

View File

@@ -0,0 +1,13 @@
// +build !online
package testutil
import (
"git.grassecon.net/urdt/ussd/internal/handlers/server"
)
var AccountService server.AccountServiceInterface
func init() {
AccountService = &server.MockAccountService{}
}

View File

@@ -0,0 +1,12 @@
//go:build online
// +build online
package testutil
import "git.grassecon.net/urdt/ussd/internal/handlers/server"
var AccountService server.AccountServiceInterface
func init() {
AccountService = &server.AccountService{}
}

View File

@@ -5,7 +5,6 @@ MOUT back 0
HALT HALT
LOAD validate_amount 64 LOAD validate_amount 64
RELOAD validate_amount RELOAD validate_amount
CATCH api_failure flag_api_call_error 1
CATCH invalid_amount flag_invalid_amount 1 CATCH invalid_amount flag_invalid_amount 1
INCMP _ 0 INCMP _ 0
LOAD get_recipient 12 LOAD get_recipient 12

View File

@@ -1 +0,0 @@
Failed to connect to the custodial service.Please try again.

View File

@@ -1,5 +0,0 @@
MOUT retry 0
MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9

View File

@@ -1,5 +1,4 @@
LOAD reset_account_authorized 0 LOAD reset_account_authorized 0
RELOAD reset_account_authorized
MOUT my_balance 1 MOUT my_balance 1
MOUT community_balance 2 MOUT community_balance 2
MOUT back 0 MOUT back 0

View File

@@ -1 +1 @@
{{.fetch_custodial_balances}} Your community balance is: 0.00SRF

View File

@@ -1,11 +1,5 @@
LOAD reset_incorrect 6 LOAD reset_incorrect 0
LOAD fetch_custodial_balances 0
CATCH api_failure flag_api_call_error 1
MAP fetch_custodial_balances
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin flag_incorrect_pin 1
CATCH pin_entry flag_account_authorized 0 CATCH pin_entry flag_account_authorized 0
MOUT back 0 LOAD quit_with_balance 0
MOUT quit 9
HALT HALT
INCMP _ 0
INCMP quit 9

View File

@@ -1 +1 @@
Confirm your new PIN: Confirm your new PIN:

View File

@@ -2,6 +2,4 @@ CATCH invalid_pin flag_valid_pin 0
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
INCMP * pin_reset_success INCMP pin_reset_success *

View File

@@ -1 +1 @@
Thibitisha PIN yako mpya: Thibitisha PIN yako mpya:

View File

@@ -18,4 +18,4 @@ INCMP select_gender 3
INCMP enter_yob 4 INCMP enter_yob 4
INCMP enter_location 5 INCMP enter_location 5
INCMP enter_offerings 6 INCMP enter_offerings 6
INCMP view_profile 7 INCMP view_profile 7

View File

@@ -1 +1 @@
Weka jina la familia Weka jina la familia

View File

@@ -7,6 +7,3 @@ HALT
RELOAD save_firstname RELOAD save_firstname
INCMP _ 0 INCMP _ 0
INCMP pin_entry * INCMP pin_entry *

View File

@@ -1 +1,2 @@
PIN mpya na udhibitisho wa pin mpya hazilingani.Tafadhali jaribu tena.Kwa usaidizi piga simu +254757628885. PIN mpya na udhibitisho wa PIN mpya hazilingani. Tafadhali jaribu tena.
Kwa usaidizi piga simu +254757628885.

View File

@@ -1,3 +1,4 @@
RELOAD reset_account_authorized
MOUT back 0 MOUT back 0
MOUT quit 9 MOUT quit 9
HALT HALT

View File

@@ -7,8 +7,6 @@ msgstr "Ombi lako limetumwa. %s atapokea %s kutoka kwa %s."
msgid "Thank you for using Sarafu. Goodbye!" msgid "Thank you for using Sarafu. Goodbye!"
msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!" msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!"
msgid "For more help,please call: 0757628885" msgid "For more help,please call: 0757628885"
msgstr "Kwa usaidizi zaidi,piga: 0757628885" msgstr "Kwa usaidizi zaidi,piga: 0757628885"
msgid "Balance: %s\n"
msgstr "Salio: %s\n"

View File

@@ -1 +1 @@
{{.check_balance}} Balance: {{.check_balance}}

View File

@@ -1,6 +1,5 @@
LOAD check_balance 64 LOAD check_balance 64
RELOAD check_balance RELOAD check_balance
CATCH api_failure flag_api_call_error 1
MAP check_balance MAP check_balance
MOUT send 1 MOUT send 1
MOUT vouchers 2 MOUT vouchers 2

View File

@@ -1 +1 @@
{{.check_balance}} Salio: {{.check_balance}}

View File

@@ -1 +1 @@
{{.fetch_custodial_balances}} Your balance is: 0.00 SRF

View File

@@ -1,11 +1,5 @@
LOAD reset_incorrect 6 LOAD reset_incorrect 0
LOAD fetch_custodial_balances 0
CATCH api_failure flag_api_call_error 1
MAP fetch_custodial_balances
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin flag_incorrect_pin 1
CATCH pin_entry flag_account_authorized 0 CATCH pin_entry flag_account_authorized 0
MOUT back 0 LOAD quit_with_balance 0
MOUT quit 9
HALT HALT
INCMP _ 0
INCMP quit 9

View File

@@ -0,0 +1 @@
My vouchers

View File

@@ -0,0 +1,6 @@
MOUT select_voucher 1
MOUT voucher_details 2
MOUT back 0
HALT
INCMP _ 0
INCMP select_voucher 1

View File

@@ -1 +1 @@
Enter a new four number PIN: Enter a new four number PIN:

View File

@@ -1,13 +1,10 @@
LOAD authorize_account 12 CATCH _ flag_allow_update 0
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
CATCH old_pin flag_allow_update 0
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
LOAD save_temporary_pin 6 LOAD save_temporary_pin 6
LOAD verify_new_pin 0
RELOAD save_temporary_pin RELOAD save_temporary_pin
LOAD verify_new_pin 8
RELOAD verify_new_pin RELOAD verify_new_pin
INCMP * confirm_pin_change CATCH incorrect_pin flag_incorrect_pin 1
INCMP confirm_pin_change *

View File

@@ -1,2 +1 @@
Weka PIN mpya ya nne nambari: Weka PIN mpya ya nne nambari:

View File

@@ -1 +1 @@
Enter your old PIN Enter your old PIN

View File

@@ -1,7 +1,9 @@
LOAD reset_allow_update 0 LOAD reset_allow_update 0
RELOAD reset_allow_update
MOUT back 0 MOUT back 0
HALT HALT
RELOAD reset_allow_update LOAD authorize_account 12
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
INCMP _ 0 INCMP _ 0
INCMP new_pin * INCMP new_pin *

View File

@@ -1 +1 @@
Weka PIN yako ya zamani: Weka PIN yako ya zamani:

View File

@@ -4,5 +4,4 @@ MOUT guard_pin 3
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
INCMP old_pin 1 INCMP old_pin 1

View File

@@ -1 +1 @@
The PIN is not a match. Try again The PIN is not a match. Try again

View File

@@ -3,4 +3,3 @@ MOUT quit 9
HALT HALT
INCMP confirm_pin_change 1 INCMP confirm_pin_change 1
INCMP quit 9 INCMP quit 9

View File

@@ -1 +1 @@
Your PIN change request has been successful Your PIN change request has been successful

View File

@@ -1,10 +1,8 @@
LOAD confirm_pin_change 0 LOAD confirm_pin_change 0
RELOAD confirm_pin_change RELOAD confirm_pin_change
CATCH pin_reset_mismatch flag_pin_mismatch 1 CATCH pin_reset_mismatch flag_pin_mismatch 1
MOUT back 0 MOUT back 0
MOUT quit 9 MOUT quit 9
HALT HALT
INCMP main 0 INCMP main 0
INCMP quit 9 INCMP quit 9

View File

@@ -1 +1 @@
Ombi lako la kubadili PIN limefanikiwa Ombi lako la kubadili PIN limefanikiwa

View File

@@ -14,4 +14,3 @@ flag,flag_valid_pin,20,this is set when the given PIN is valid
flag,flag_allow_update,21,this is set to allow a user to update their profile data flag,flag_allow_update,21,this is set to allow a user to update their profile data
flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth
flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid
flag,flag_api_call_error,25,this is set when communication to an external service fails
1 flag flag_language_set 8 checks whether the user has set their prefered language
14 flag flag_allow_update 21 this is set to allow a user to update their profile data
15 flag flag_single_edit 22 this is set to allow a user to edit a single profile item such as year of birth
16 flag flag_incorrect_date_format 23 this is set when the given year of birth is invalid
flag flag_api_call_error 25 this is set when communication to an external service fails

View File

@@ -1 +1 @@
Profile updated successfully Profile updated successfully

View File

@@ -1 +1 @@
Ombi la Kuweka wasifu limefanikiwa Ombi la Kuweka wasifu limefanikiwa

View File

@@ -1,8 +1,6 @@
CATCH select_language flag_language_set 0 CATCH select_language flag_language_set 0
CATCH terms flag_account_created 0 CATCH terms flag_account_created 0
LOAD check_account_status 0 LOAD check_account_status 0
RELOAD check_account_status
CATCH api_failure flag_api_call_error 1
CATCH account_pending flag_account_pending 1 CATCH account_pending flag_account_pending 1
CATCH create_pin flag_pin_set 0 CATCH create_pin flag_pin_set 0
CATCH main flag_account_success 1 CATCH main flag_account_success 1

View File

@@ -0,0 +1 @@
Select voucher

View File

@@ -1 +0,0 @@
Angalia Wasifu

View File

@@ -0,0 +1 @@
Voucher details

View File

@@ -17,19 +17,19 @@
}, },
{ {
"input": "1", "input": "1",
"expectedContent": "Enter your old PIN\n\n0:Back" "expectedContent": "Enter your old PIN\n0:Back"
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Enter a new four number PIN:\n\n0:Back" "expectedContent": "Enter a new four number PIN:\n0:Back"
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Confirm your new PIN:\n\n0:Back" "expectedContent": "Confirm your new PIN:\n0:Back"
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Your PIN change request has been successful\n\n0:Back\n9:Quit" "expectedContent": "Your PIN change request has been successful\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@@ -74,98 +74,6 @@
} }
] ]
}, },
{
"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", "name": "menu_my_account_edit_firstname",
"steps": [ "steps": [
@@ -191,7 +99,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" "expectedContent": "Profile updated successfully\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@@ -232,7 +140,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" "expectedContent": "Profile updated successfully\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@@ -274,7 +182,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" "expectedContent": "Profile updated successfully\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@@ -315,7 +223,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" "expectedContent": "Profile updated successfully\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@@ -356,7 +264,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" "expectedContent": "Profile updated successfully\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@@ -397,7 +305,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" "expectedContent": "Profile updated successfully\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",

View File

@@ -1,4 +1,4 @@
package menutraversaltest package main
import ( import (
"bytes" "bytes"
@@ -131,6 +131,82 @@ func TestAccountRegistrationRejectTerms(t *testing.T) {
} }
} }
func TestSendWithInvalidInputs(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_invalid_inputs")
for _, group := range groups {
for _, step := range group.Steps {
cont, err := en.Exec(ctx, []byte(step.Input))
if err != nil {
t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err)
return
}
if !cont {
break
}
w := bytes.NewBuffer(nil)
if _, err := en.Flush(ctx, w); err != nil {
t.Fatalf("Test case '%s' failed during Flush: %v", group.Name, err)
}
b := w.Bytes()
// Extract the dynamic public key from the output
publicKey := extractPublicKey(b)
// Replace placeholder {public_key} with the actual dynamic public key
expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1)
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 TestMyAccount_Check_My_Balance(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_check_my_balance")
for _, group := range groups {
for index, step := range group.Steps {
t.Logf("step %v with input %v", index, step.Input)
cont, err := en.Exec(ctx, []byte(step.Input))
if err != nil {
t.Errorf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err)
return
}
if !cont {
break
}
w := bytes.NewBuffer(nil)
if _, err := en.Flush(ctx, w); err != nil {
t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err)
}
b := w.Bytes()
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) { func TestMainMenuHelp(t *testing.T) {
en, fn, _ := testutil.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
@@ -201,6 +277,41 @@ func TestMainMenuQuit(t *testing.T) {
} }
} }
func TestMyAccount_Check_Community_Balance(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_check_community_balance")
for _, group := range groups {
for index, step := range group.Steps {
t.Logf("step %v with input %v", index, step.Input)
cont, err := en.Exec(ctx, []byte(step.Input))
if err != nil {
t.Errorf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err)
return
}
if !cont {
break
}
w := bytes.NewBuffer(nil)
if _, err := en.Flush(ctx, w); err != nil {
t.Errorf("Test case '%s' failed during Flush: %v", group.Name, err)
}
b := w.Bytes()
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) { func TestMyAccount_MyAddress(t *testing.T) {
en, fn, _ := testutil.TestEngine(sessionID) en, fn, _ := testutil.TestEngine(sessionID)
defer fn() defer fn()
@@ -227,7 +338,6 @@ func TestMyAccount_MyAddress(t *testing.T) {
publicKey := extractPublicKey(b) publicKey := extractPublicKey(b)
expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1) expectedContent := bytes.Replace([]byte(step.ExpectedContent), []byte("{public_key}"), []byte(publicKey), -1)
step.ExpectedContent = string(expectedContent)
match, err := step.MatchesExpectedContent(b) match, err := step.MatchesExpectedContent(b)
if err != nil { if err != nil {
t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err) t.Fatalf("Error compiling regex for step '%s': %v", step.Input, err)

View File

@@ -127,6 +127,72 @@
} }
] ]
}, },
{
"name": "menu_my_account_check_my_balance",
"steps": [
{
"input": "",
"expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
},
{
"input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
},
{
"input": "3",
"expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back"
},
{
"input": "1",
"expectedContent": "Please enter your PIN:"
},
{
"input": "1235",
"expectedContent": "Incorrect pin\n1:retry\n9:Quit"
},
{
"input": "1",
"expectedContent": "Please enter your PIN:"
},
{
"input": "1234",
"expectedContent": "Your balance is: 0.00 SRFYour account balance is 0.003 CELO"
}
]
},
{
"name": "menu_my_account_check_community_balance",
"steps": [
{
"input": "",
"expectedContent": "Balance: 0.003 CELO\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
},
{
"input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
},
{
"input": "3",
"expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back"
},
{
"input": "2",
"expectedContent": "Please enter your PIN:"
},
{
"input": "1235",
"expectedContent": "Incorrect pin\n1:retry\n9:Quit"
},
{
"input": "1",
"expectedContent": "Please enter your PIN:"
},
{
"input": "1234",
"expectedContent": "Your balance is: 0.00 SRFYour account balance is 0.003 CELO"
}
]
},
{ {
"name": "menu_my_account_my_address", "name": "menu_my_account_my_address",
"steps": [ "steps": [