Compare commits

..

18 Commits

Author SHA1 Message Date
lash
a7ca280964 Merge branch 'master' into lash/purify-max 2025-01-10 11:40:49 +00:00
8f5ed0cd4f Merge pull request 'postgres-switch-for-tests' (#255) from postgres-switch-for-tests into master
Reviewed-on: urdt/ussd#255
2025-01-10 12:07:07 +01:00
lash
f1664a43a8
Rename module, export storage, testutil 2025-01-10 10:53:27 +00:00
c29abfe21e Merge branch 'master' into postgres-switch-for-tests 2025-01-10 13:43:16 +03:00
9a6d8e5158
Refactored the code to switch between postgres and gdbm, with db cleanup 2025-01-10 13:41:05 +03:00
3fccfaab61
Replace the connStr if it is not set 2025-01-09 13:01:28 +03:00
lash
b50a51df9b
Implement postgres schema 2025-01-09 07:42:09 +00:00
lash
df8c9aab0c
Rehabilitate tests 2025-01-08 22:27:19 +00:00
lash
ddefdd7fb3 Merge branch 'lash/purify-more' into postgres-switch-for-tests 2025-01-08 22:05:12 +00:00
4b5f08e25e
Merge branch 'master' into postgres-switch-for-tests 2025-01-08 11:06:15 +03:00
ea9cab930e
cleanup the generated test data for the schema 2025-01-08 10:59:22 +03:00
a37f6e6da3
pass the dbschema in the context 2025-01-08 10:57:58 +03:00
f59c3a53ef
allow the BuildConnStr to be accessed by different packages 2025-01-08 10:56:59 +03:00
81c3378ea6
use a flag to pass the schema to the context 2025-01-08 10:55:43 +03:00
46a6d2bc6e
create a schema if it does not exist and use it in the connection 2025-01-08 10:37:47 +03:00
c12e867ac3
add a db flag to specify the database of choice 2025-01-06 15:06:25 +03:00
79de0a9092
pass the base directory to load the .env file 2025-01-06 14:54:04 +03:00
3ee15497a5
specify the base directory for loading the .env file 2025-01-06 14:50:39 +03:00
58 changed files with 348 additions and 5660 deletions

View File

@ -6,7 +6,7 @@ import (
"os"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/internal/utils"
"git.grassecon.net/grassrootseconomics/visedriver/internal/utils"
)
var (

View File

@ -4,7 +4,7 @@ import (
"context"
"log"
"git.grassecon.net/urdt/ussd/devtools/admin/commands"
"git.grassecon.net/grassrootseconomics/visedriver/devtools/admin/commands"
)
func main() {

View File

@ -1,201 +0,0 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"path"
"syscall"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/lang"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/handlers"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/remote"
"git.grassecon.net/urdt/ussd/request"
"git.grassecon.net/urdt/ussd/internal/args"
)
var (
logg = logging.NewVanilla()
scriptDir = path.Join("services", "registration")
menuSeparator = ": "
)
func init() {
initializers.LoadEnvVariables()
}
type asyncRequestParser struct {
sessionId string
input []byte
}
func (p *asyncRequestParser) GetSessionId(ctx context.Context, r any) (string, error) {
return p.sessionId, nil
}
func (p *asyncRequestParser) GetInput(r any) ([]byte, error) {
return p.input, nil
}
func main() {
config.LoadConfig()
var connStr string
var sessionId string
var resourceDir string
var size uint
var database string
var engineDebug bool
var host string
var port uint
var err error
var gettextDir string
var langs args.LangVar
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.StringVar(&connStr, "c", "", "connection string")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.Parse()
if connStr != "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
logg.Infof("start command", "conn", connData, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId)
ctx := context.Background()
ctx = context.WithValue(ctx, "Database", database)
ln, err := lang.LanguageFromCode(config.DefaultLanguage)
if err != nil {
fmt.Fprintf(os.Stderr, "default language set error: %v", err)
os.Exit(1)
}
ctx = context.WithValue(ctx, "Language", ln)
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
}
if engineDebug {
cfg.EngineDebug = true
}
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
rs, err := menuStorageService.GetResource(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
defer userdataStore.Close()
dbResource, ok := rs.(*resource.DbResource)
if !ok {
os.Exit(1)
}
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore)
accountService := remote.AccountService{}
hl, err := lhs.GetHandler(&accountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
stateStore, err := menuStorageService.GetStateStore(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
defer stateStore.Close()
rp := &asyncRequestParser{
sessionId: sessionId,
}
sh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
cfg.SessionId = sessionId
rqs := request.RequestSession{
Ctx: ctx,
Writer: os.Stdout,
Config: cfg,
}
cint := make(chan os.Signal)
cterm := make(chan os.Signal)
signal.Notify(cint, os.Interrupt, syscall.SIGINT)
signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
go func() {
select {
case _ = <-cint:
case _ = <-cterm:
}
sh.Shutdown()
}()
for true {
rqs, err = sh.Process(rqs)
if err != nil {
logg.ErrorCtxf(ctx, "error in process: %v", "err", err)
fmt.Errorf("error in process: %v", err)
os.Exit(1)
}
rqs, err = sh.Output(rqs)
if err != nil {
logg.ErrorCtxf(ctx, "error in output: %v", "err", err)
fmt.Errorf("error in output: %v", err)
os.Exit(1)
}
rqs, err = sh.Reset(rqs)
if err != nil {
logg.ErrorCtxf(ctx, "error in reset: %v", "err", err)
fmt.Errorf("error in reset: %v", err)
os.Exit(1)
}
fmt.Println("")
_, err = fmt.Scanln(&rqs.Input)
if err != nil {
logg.ErrorCtxf(ctx, "error in input", "err", err)
fmt.Errorf("error in input: %v", err)
os.Exit(1)
}
}
}

View File

@ -1,166 +0,0 @@
package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"path"
"strconv"
"syscall"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/lang"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/handlers"
httpserver "git.grassecon.net/urdt/ussd/internal/http"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/remote"
"git.grassecon.net/urdt/ussd/internal/args"
)
var (
logg = logging.NewVanilla()
scriptDir = path.Join("services", "registration")
menuSeparator = ": "
)
func init() {
initializers.LoadEnvVariables()
}
func main() {
config.LoadConfig()
var connStr string
var resourceDir string
var size uint
var database string
var engineDebug bool
var host string
var port uint
var err error
var gettextDir string
var langs args.LangVar
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.StringVar(&connStr, "c", "", "connection string")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.Parse()
if connStr != "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
logg.Infof("start command", "conn", connData, "resourcedir", resourceDir, "outputsize", size)
ctx := context.Background()
ctx = context.WithValue(ctx, "Database", database)
ln, err := lang.LanguageFromCode(config.DefaultLanguage)
if err != nil {
fmt.Fprintf(os.Stderr, "default language set error: %v", err)
os.Exit(1)
}
ctx = context.WithValue(ctx, "Language", ln)
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
}
if engineDebug {
cfg.EngineDebug = true
}
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
rs, err := menuStorageService.GetResource(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
defer userdataStore.Close()
dbResource, ok := rs.(*resource.DbResource)
if !ok {
os.Exit(1)
}
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
accountService := remote.AccountService{}
hl, err := lhs.GetHandler(&accountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
stateStore, err := menuStorageService.GetStateStore(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
defer stateStore.Close()
rp := &httpserver.DefaultRequestParser{}
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
// TODO: less hacky way of making session handler
//sh := request.ToSessionHandler(bsh)
sh := &httpserver.SessionHandler{
RequestHandler: bsh,
}
s := &http.Server{
Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
Handler: sh,
}
s.RegisterOnShutdown(sh.Shutdown)
cint := make(chan os.Signal)
cterm := make(chan os.Signal)
signal.Notify(cint, os.Interrupt, syscall.SIGINT)
signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
go func() {
select {
case _ = <-cint:
case _ = <-cterm:
}
s.Shutdown(ctx)
}()
err = s.ListenAndServe()
if err != nil {
logg.Infof("Server closed with error", "err", err)
}
}

View File

@ -10,8 +10,8 @@ import (
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/lang"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/grassrootseconomics/visedriver/config"
"git.grassecon.net/grassrootseconomics/visedriver/initializers"
)
const (

View File

@ -1,148 +0,0 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"path"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/lang"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/handlers"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/internal/args"
"git.grassecon.net/urdt/ussd/remote"
)
var (
logg = logging.NewVanilla()
scriptDir = path.Join("services", "registration")
menuSeparator = ": "
)
func init() {
initializers.LoadEnvVariables()
}
// TODO: external script automatically generate language handler list from select language vise code OR consider dynamic menu generation script possibility
func main() {
config.LoadConfig()
var connStr string
var size uint
var sessionId string
var database string
var engineDebug bool
var resourceDir string
var err error
var gettextDir string
var langs args.LangVar
flag.StringVar(&resourceDir, "resourcedir", scriptDir, "resource dir")
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&connStr, "c", "", "connection string")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.Parse()
if connStr != "" {
connStr = config.DbConn
}
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
logg.Infof("start command", "conn", connData, "outputsize", size)
if len(langs.Langs()) == 0 {
langs.Set(config.DefaultLanguage)
}
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId)
ctx = context.WithValue(ctx, "Database", database)
ln, err := lang.LanguageFromCode(config.DefaultLanguage)
if err != nil {
fmt.Fprintf(os.Stderr, "default language set error: %v", err)
os.Exit(1)
}
ctx = context.WithValue(ctx, "Language", ln)
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
SessionId: sessionId,
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
}
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
if gettextDir != "" {
menuStorageService = menuStorageService.WithGettext(gettextDir, langs.Langs())
}
rs, err := menuStorageService.GetResource(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
pe, err := menuStorageService.GetPersister(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
userdatastore, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdatastore)
lhs.SetPersister(pe)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
accountService := remote.AccountService{}
hl, err := lhs.GetHandler(&accountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
en := lhs.GetEngine()
en = en.WithFirst(hl.Init)
if engineDebug {
en = en.WithDebug(nil)
}
err = engine.Loop(ctx, en, os.Stdin, os.Stdout, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err)
os.Exit(1)
}
}

View File

@ -1,34 +0,0 @@
# URDT-USSD SSH server
An SSH server entry point for the vise engine.
## Adding public keys for access
Map your (client) public key to a session identifier (e.g. phone number)
```
go run -v -tags logtrace ./cmd/ssh/sshkey/main.go -i <session_id> [--dbdir <dbpath>] <client_publickey_filepath>
```
## Create a private key for the server
```
ssh-keygen -N "" -f <server_privatekey_filepath>
```
## Run the server
```
go run -v -tags logtrace ./cmd/ssh/main.go -h <host> -p <port> [--dbdir <dbpath>] <server_privatekey_filepath>
```
## Connect to the server
```
ssh [-v] -T -p <port> -i <client_publickey_filepath> <host>
```

View File

@ -1,144 +0,0 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"path"
"sync"
"syscall"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/ssh"
"git.grassecon.net/urdt/ussd/internal/storage"
)
var (
wg sync.WaitGroup
keyStore db.Db
logg = logging.NewVanilla()
scriptDir = path.Join("services", "registration")
build = "dev"
)
func init() {
initializers.LoadEnvVariables()
}
func main() {
config.LoadConfig()
var connStr string
var authConnStr string
var resourceDir string
var size uint
var engineDebug bool
var stateDebug bool
var host string
var port uint
flag.StringVar(&connStr, "c", "", "connection string")
flag.StringVar(&authConnStr, "authdb", "", "auth connection string")
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&host, "h", "127.0.0.1", "socket host")
flag.UintVar(&port, "p", 7122, "socket port")
flag.Parse()
if connStr == "" {
connStr = config.DbConn
}
if authConnStr == "" {
authConnStr = connStr
}
connData, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
authConnData, err := storage.ToConnData(authConnStr)
if err != nil {
fmt.Fprintf(os.Stderr, "auth connstr err: %v", err)
os.Exit(1)
}
sshKeyFile := flag.Arg(0)
_, err = os.Stat(sshKeyFile)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot open ssh server private key file: %v\n", err)
os.Exit(1)
}
ctx := context.Background()
logg.WarnCtxf(ctx, "!!!!! WARNING WARNING WARNING")
logg.WarnCtxf(ctx, "!!!!! =======================")
logg.WarnCtxf(ctx, "!!!!! This is not a production ready server!")
logg.WarnCtxf(ctx, "!!!!! Do not expose to internet and only use with tunnel!")
logg.WarnCtxf(ctx, "!!!!! (See ssh -L <...>)")
logg.Infof("start command", "conn", connData, "authconn", authConnData, "resourcedir", resourceDir, "outputsize", size, "keyfile", sshKeyFile, "host", host, "port", port)
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(16),
}
if stateDebug {
cfg.StateDebug = true
}
if engineDebug {
cfg.EngineDebug = true
}
authKeyStore, err := ssh.NewSshKeyStore(ctx, authConnData.String())
if err != nil {
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
os.Exit(1)
}
defer func() {
logg.TraceCtxf(ctx, "shutdown auth key store reached")
err = authKeyStore.Close()
if err != nil {
logg.ErrorCtxf(ctx, "keystore close error", "err", err)
}
}()
cint := make(chan os.Signal)
cterm := make(chan os.Signal)
signal.Notify(cint, os.Interrupt, syscall.SIGINT)
signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
runner := &ssh.SshRunner{
Cfg: cfg,
Debug: engineDebug,
FlagFile: pfp,
Conn: connData,
ResourceDir: resourceDir,
SrvKeyFile: sshKeyFile,
Host: host,
Port: port,
}
go func() {
select {
case _ = <-cint:
case _ = <-cterm:
}
logg.TraceCtxf(ctx, "shutdown runner reached")
err := runner.Stop()
if err != nil {
logg.ErrorCtxf(ctx, "runner stop error", "err", err)
}
}()
runner.Run(ctx, authKeyStore)
}

View File

@ -1,44 +0,0 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"git.grassecon.net/urdt/ussd/internal/ssh"
)
func main() {
var dbDir string
var sessionId string
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
flag.StringVar(&sessionId, "i", "", "session id")
flag.Parse()
if sessionId == "" {
fmt.Fprintf(os.Stderr, "empty session id\n")
os.Exit(1)
}
ctx := context.Background()
sshKeyFile := flag.Arg(0)
if sshKeyFile == "" {
fmt.Fprintf(os.Stderr, "missing key file argument\n")
os.Exit(1)
}
store, err := ssh.NewSshKeyStore(ctx, dbDir)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
defer store.Close()
err = store.AddFromFile(ctx, sshKeyFile, sessionId)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}

View File

@ -7,10 +7,10 @@ import (
"os"
"path"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/debug"
"git.grassecon.net/grassrootseconomics/visedriver/config"
"git.grassecon.net/grassrootseconomics/visedriver/initializers"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"git.grassecon.net/grassrootseconomics/visedriver/debug"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/logging"
)

View File

@ -9,14 +9,15 @@ import (
"path"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/urdt/ussd/common"
"git.grassecon.net/grassrootseconomics/visedriver/config"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"git.grassecon.net/grassrootseconomics/visedriver/initializers"
"git.grassecon.net/grassrootseconomics/visedriver/common"
)
var (
logg = logging.NewVanilla()
baseDir = testdataloader.GetBasePath()
scriptDir = path.Join("services", "registration")
)
@ -24,7 +25,6 @@ func init() {
initializers.LoadEnvVariables()
}
func main() {
config.LoadConfig()
@ -86,5 +86,4 @@ func main() {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
}

View File

@ -7,8 +7,8 @@ import (
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/persist"
"git.grassecon.net/urdt/ussd/internal/storage"
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
dbstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db"
)
var (

View File

@ -6,7 +6,7 @@ import (
"strings"
"time"
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db"
dbstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)

View File

@ -6,7 +6,7 @@ import (
"math/big"
"strings"
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db"
dbstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)

View File

@ -10,7 +10,7 @@ import (
visedb "git.defalsify.org/vise.git/db"
memdb "git.defalsify.org/vise.git/db/mem"
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db"
dbstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)

View File

@ -4,7 +4,7 @@ import (
"net/url"
"strings"
"git.grassecon.net/urdt/ussd/initializers"
"git.grassecon.net/grassrootseconomics/visedriver/initializers"
)
const (

View File

@ -4,7 +4,7 @@ import (
"fmt"
"encoding/binary"
"git.grassecon.net/urdt/ussd/common"
"git.grassecon.net/grassrootseconomics/visedriver/common"
"git.defalsify.org/vise.git/db"
)

View File

@ -5,7 +5,7 @@ package debug
import (
"git.defalsify.org/vise.git/db"
"git.grassecon.net/urdt/ussd/common"
"git.grassecon.net/grassrootseconomics/visedriver/common"
)
func init() {

View File

@ -3,7 +3,7 @@ package debug
import (
"testing"
"git.grassecon.net/urdt/ussd/common"
"git.grassecon.net/grassrootseconomics/visedriver/common"
"git.defalsify.org/vise.git/db"
)

View File

@ -1,7 +1,7 @@
package errors
import (
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/grassrootseconomics/visedriver/internal/handlers"
)
var (

2
go.mod
View File

@ -1,4 +1,4 @@
module git.grassecon.net/urdt/ussd
module git.grassecon.net/grassrootseconomics/visedriver
go 1.23.0

View File

@ -7,10 +7,10 @@ import (
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/request"
"git.grassecon.net/urdt/ussd/errors"
"git.grassecon.net/urdt/ussd/internal/handlers/application"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/grassrootseconomics/visedriver/request"
"git.grassecon.net/grassrootseconomics/visedriver/errors"
"git.grassecon.net/grassrootseconomics/visedriver/internal/handlers/application"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
)
var (

View File

@ -1,141 +0,0 @@
package handlers
import (
"context"
"strings"
"git.defalsify.org/vise.git/asm"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers/application"
"git.grassecon.net/urdt/ussd/internal/utils"
"git.grassecon.net/urdt/ussd/remote"
)
type HandlerService interface {
GetHandler() (*application.Handlers, error)
}
func getParser(fp string, debug bool) (*asm.FlagParser, error) {
flagParser := asm.NewFlagParser().WithDebug()
_, err := flagParser.Load(fp)
if err != nil {
return nil, err
}
return flagParser, nil
}
type LocalHandlerService struct {
Parser *asm.FlagParser
DbRs *resource.DbResource
Pe *persist.Persister
UserdataStore *db.Db
AdminStore *utils.AdminStore
Cfg engine.Config
Rs resource.Resource
}
func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) {
parser, err := getParser(fp, debug)
if err != nil {
return nil, err
}
adminstore, err := utils.NewAdminStore(ctx, "admin_numbers")
if err != nil {
return nil, err
}
return &LocalHandlerService{
Parser: parser,
DbRs: dbResource,
AdminStore: adminstore,
Cfg: cfg,
Rs: rs,
}, nil
}
func (ls *LocalHandlerService) SetPersister(Pe *persist.Persister) {
ls.Pe = Pe
}
func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
ls.UserdataStore = db
}
func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*application.Handlers, error) {
replaceSeparatorFunc := func(input string) string {
return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator)
}
appHandlers, err := application.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparatorFunc)
if err != nil {
return nil, err
}
appHandlers = appHandlers.WithPersister(ls.Pe)
ls.DbRs.AddLocalFunc("set_language", appHandlers.SetLanguage)
ls.DbRs.AddLocalFunc("create_account", appHandlers.CreateAccount)
ls.DbRs.AddLocalFunc("save_temporary_pin", appHandlers.SaveTemporaryPin)
ls.DbRs.AddLocalFunc("verify_create_pin", appHandlers.VerifyCreatePin)
ls.DbRs.AddLocalFunc("check_identifier", appHandlers.CheckIdentifier)
ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus)
ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize)
ls.DbRs.AddLocalFunc("quit", appHandlers.Quit)
ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance)
ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient)
ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset)
ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient)
ls.DbRs.AddLocalFunc("max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount)
ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount)
ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient)
ls.DbRs.AddLocalFunc("get_sender", appHandlers.GetSender)
ls.DbRs.AddLocalFunc("get_amount", appHandlers.GetAmount)
ls.DbRs.AddLocalFunc("reset_incorrect", appHandlers.ResetIncorrectPin)
ls.DbRs.AddLocalFunc("save_firstname", appHandlers.SaveFirstname)
ls.DbRs.AddLocalFunc("save_familyname", appHandlers.SaveFamilyname)
ls.DbRs.AddLocalFunc("save_gender", appHandlers.SaveGender)
ls.DbRs.AddLocalFunc("save_location", appHandlers.SaveLocation)
ls.DbRs.AddLocalFunc("save_yob", appHandlers.SaveYob)
ls.DbRs.AddLocalFunc("save_offerings", appHandlers.SaveOfferings)
ls.DbRs.AddLocalFunc("reset_account_authorized", appHandlers.ResetAccountAuthorized)
ls.DbRs.AddLocalFunc("reset_allow_update", appHandlers.ResetAllowUpdate)
ls.DbRs.AddLocalFunc("get_profile_info", appHandlers.GetProfileInfo)
ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob)
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob)
ls.DbRs.AddLocalFunc("initiate_transaction", appHandlers.InitiateTransaction)
ls.DbRs.AddLocalFunc("verify_new_pin", appHandlers.VerifyNewPin)
ls.DbRs.AddLocalFunc("confirm_pin_change", appHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance)
ls.DbRs.AddLocalFunc("set_default_voucher", appHandlers.SetDefaultVoucher)
ls.DbRs.AddLocalFunc("check_vouchers", appHandlers.CheckVouchers)
ls.DbRs.AddLocalFunc("get_vouchers", appHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher)
ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher)
ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails)
ls.DbRs.AddLocalFunc("reset_valid_pin", appHandlers.ResetValidPin)
ls.DbRs.AddLocalFunc("check_pin_mismatch", appHandlers.CheckBlockedNumPinMisMatch)
ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber)
ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber)
ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber)
ls.DbRs.AddLocalFunc("reset_others_pin", appHandlers.ResetOthersPin)
ls.DbRs.AddLocalFunc("save_others_temporary_pin", appHandlers.SaveOthersTemporaryPin)
ls.DbRs.AddLocalFunc("get_current_profile_info", appHandlers.GetCurrentProfileInfo)
ls.DbRs.AddLocalFunc("check_transactions", appHandlers.CheckTransactions)
ls.DbRs.AddLocalFunc("get_transactions", appHandlers.GetTransactionsList)
ls.DbRs.AddLocalFunc("view_statement", appHandlers.ViewTransactionStatement)
ls.DbRs.AddLocalFunc("update_all_profile_items", appHandlers.UpdateAllProfileItems)
ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack)
ls.DbRs.AddLocalFunc("show_blocked_account", appHandlers.ShowBlockedAccount)
return appHandlers, nil
}
// TODO: enable setting of sessionId on engine init time
func (ls *LocalHandlerService) GetEngine() *engine.DefaultEngine {
en := engine.NewEngine(ls.Cfg, ls.Rs)
en = en.WithPersister(ls.Pe)
return en
}

View File

@ -3,24 +3,30 @@ package initializers
import (
"log"
"os"
"path"
"strconv"
"github.com/joho/godotenv"
)
func LoadEnvVariables() {
err := godotenv.Load()
LoadEnvVariablesPath(".")
}
func LoadEnvVariablesPath(dir string) {
fp := path.Join(dir, ".env")
err := godotenv.Load(fp)
if err != nil {
log.Fatal("Error loading .env file")
log.Fatal("Error loading .env file", err)
}
}
// Helper to get environment variables with a default fallback
func GetEnv(key, defaultVal string) string {
if value, exists := os.LookupEnv(key); exists {
return value
if value, exists := os.LookupEnv(key); exists {
return value
}
return defaultVal
return defaultVal
}
// Helper to safely convert environment variables to uint

View File

@ -1,34 +0,0 @@
package args
import (
"strings"
"git.defalsify.org/vise.git/lang"
)
type LangVar struct {
v []lang.Language
}
func(lv *LangVar) Set(s string) error {
v, err := lang.LanguageFromCode(s)
if err != nil {
return err
}
lv.v = append(lv.v, v)
return err
}
func(lv *LangVar) String() string {
var s []string
for _, v := range(lv.v) {
s = append(s, v.Code)
}
return strings.Join(s, ",")
}
func(lv *LangVar) Langs() []lang.Language {
return lv.v
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ import (
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
)
var (

View File

@ -5,7 +5,7 @@ import (
"io/ioutil"
"net/http"
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/grassrootseconomics/visedriver/internal/handlers"
)
type DefaultRequestParser struct {

View File

@ -6,8 +6,8 @@ import (
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/request"
"git.grassecon.net/grassrootseconomics/visedriver/internal/handlers"
"git.grassecon.net/grassrootseconomics/visedriver/request"
)
var (

View File

@ -9,9 +9,9 @@ import (
"testing"
"git.defalsify.org/vise.git/engine"
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/testutil/mocks/httpmocks"
"git.grassecon.net/urdt/ussd/request"
"git.grassecon.net/grassrootseconomics/visedriver/internal/handlers"
"git.grassecon.net/grassrootseconomics/visedriver/testutil/mocks/httpmocks"
"git.grassecon.net/grassrootseconomics/visedriver/request"
)
// invalidRequestType is a custom type to test invalid request scenarios

View File

@ -10,8 +10,8 @@ import (
"git.defalsify.org/vise.git/db"
"git.grassecon.net/urdt/ussd/internal/storage"
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db/gdbm"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
dbstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db/gdbm"
)
type SshKeyStore struct {

View File

@ -17,9 +17,9 @@ import (
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state"
"git.grassecon.net/urdt/ussd/handlers"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/remote"
"git.grassecon.net/grassrootseconomics/visedriver/handlers"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"git.grassecon.net/grassrootseconomics/visedriver/remote"
)
var (

View File

@ -1,127 +0,0 @@
package testutil
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"time"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/handlers"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/urdt/ussd/internal/testutil/testservice"
"git.grassecon.net/urdt/ussd/internal/testutil/testtag"
testdataloader "github.com/peteole/testdata-loader"
"git.grassecon.net/urdt/ussd/remote"
)
var (
baseDir = testdataloader.GetBasePath()
logg = logging.NewVanilla()
scriptDir = path.Join(baseDir, "services", "registration")
)
func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId)
pfp := path.Join(scriptDir, "pp.csv")
var eventChannel = make(chan bool)
cfg := engine.Config{
Root: "root",
SessionId: sessionId,
OutputSize: uint32(160),
FlagCount: uint32(128),
}
connStr, err := filepath.Abs(".test_state/state.gdbm")
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
conn, err := storage.ToConnData(connStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr parse err: %v", err)
os.Exit(1)
}
resourceDir := scriptDir
menuStorageService := storage.NewMenuStorageService(conn, resourceDir)
rs, err := menuStorageService.GetResource(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "resource error: %v", err)
os.Exit(1)
}
pe, err := menuStorageService.GetPersister(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "persister error: %v", err)
os.Exit(1)
}
userDataStore, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "userdb error: %v", err)
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
fmt.Fprintf(os.Stderr, "dbresource cast error")
os.Exit(1)
}
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userDataStore)
lhs.SetPersister(pe)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
if testtag.AccountService == nil {
testtag.AccountService = &remote.AccountService{}
}
switch testtag.AccountService.(type) {
case *testservice.TestAccountService:
go func() {
eventChannel <- false
}()
case *remote.AccountService:
go func() {
time.Sleep(5 * time.Second) // Wait for 5 seconds
eventChannel <- true
}()
default:
panic("Unknown account service type")
}
hl, err := lhs.GetHandler(testtag.AccountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
en := lhs.GetEngine()
en = en.WithFirst(hl.Init)
cleanFn := func() {
err := en.Finish()
if err != nil {
logg.Errorf(err.Error())
}
err = menuStorageService.Close()
if err != nil {
logg.Errorf(err.Error())
}
logg.Infof("testengine storage closed")
}
return en, cleanFn, eventChannel
}

View File

@ -3,7 +3,7 @@ package mocks
import (
"context"
"git.grassecon.net/urdt/ussd/models"
"git.grassecon.net/grassrootseconomics/visedriver/models"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
"github.com/stretchr/testify/mock"
)

View File

@ -1,12 +0,0 @@
// +build !online
package testtag
import (
"git.grassecon.net/urdt/ussd/remote"
accountservice "git.grassecon.net/urdt/ussd/internal/testutil/testservice"
)
var (
AccountService remote.AccountServiceInterface = &accountservice.TestAccountService{}
)

View File

@ -6,13 +6,11 @@ import (
"flag"
"log"
"math/rand"
"os"
"path/filepath"
"regexp"
"testing"
"git.grassecon.net/urdt/ussd/internal/testutil"
"git.grassecon.net/urdt/ussd/internal/testutil/driver"
"git.grassecon.net/grassrootseconomics/visedriver/internal/testutil"
"git.grassecon.net/grassrootseconomics/visedriver/internal/testutil/driver"
"github.com/gofrs/uuid"
)
@ -24,11 +22,9 @@ var (
)
var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests")
func testStore() string {
v, _ := filepath.Abs(".test_state/state.gdbm")
return v
}
var database = flag.String("db", "gdbm", "Specify the database (gdbm or postgres)")
var connStr = flag.String("conn", ".test_state", "connection string")
var dbSchema = flag.String("schema", "test", "Specify the database schema (default test)")
func GenerateSessionId() string {
uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g))
@ -84,12 +80,15 @@ func extractSendAmount(response []byte) string {
}
func TestMain(m *testing.M) {
// Parse the flags
flag.Parse()
sessionID = GenerateSessionId()
defer func() {
if err := os.RemoveAll(testStore()); err != nil {
log.Fatalf("Failed to delete state store %s: %v", testStore(), err)
}
}()
// set the db
testutil.SetDatabase(*database, *connStr, *dbSchema)
// Cleanup the db after tests
defer testutil.CleanDatabase()
m.Run()
}
@ -126,7 +125,6 @@ func TestAccountCreationSuccessful(t *testing.T) {
}
}
<-eventChannel
}
func TestAccountRegistrationRejectTerms(t *testing.T) {

View File

@ -10,8 +10,8 @@ import (
"net/http"
"net/url"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/models"
"git.grassecon.net/grassrootseconomics/visedriver/config"
"git.grassecon.net/grassrootseconomics/visedriver/models"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)

View File

@ -10,7 +10,7 @@ import (
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/urdt/ussd/internal/storage"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
)
var (

View File

@ -15,6 +15,7 @@ const (
type ConnData struct {
typ int
str string
domain string
}
func (cd *ConnData) DbType() int {
@ -25,23 +26,38 @@ func (cd *ConnData) String() string {
return cd.str
}
func probePostgres(s string) (string, bool) {
v, err := url.Parse(s)
if err != nil {
return "", false
}
if v.Scheme != "postgres" {
return "", false
}
return s, true
func (cd *ConnData) Domain() string {
return cd.domain
}
func probeGdbm(s string) (string, bool) {
func (cd *ConnData) Path() string {
v, _ := url.Parse(cd.str)
v.RawQuery = ""
return v.String()
}
func probePostgres(s string) (string, string, bool) {
domain := "public"
v, err := url.Parse(s)
if err != nil {
return "", "", false
}
if v.Scheme != "postgres" {
return "", "", false
}
vv := v.Query()
if vv.Has("search_path") {
domain = vv.Get("search_path")
}
return s, domain, true
}
func probeGdbm(s string) (string, string, bool) {
if !path.IsAbs(s) {
return "", false
return "", "", false
}
s = path.Clean(s)
return s, true
return s, "", true
}
func ToConnData(connStr string) (ConnData, error) {
@ -51,14 +67,15 @@ func ToConnData(connStr string) (ConnData, error) {
return o, nil
}
v, ok := probePostgres(connStr)
v, domain, ok := probePostgres(connStr)
if ok {
o.typ = DBTYPE_POSTGRES
o.str = v
o.domain = domain
return o, nil
}
v, ok = probeGdbm(connStr)
v, _, ok = probeGdbm(connStr)
if ok {
o.typ = DBTYPE_GDBM
o.str = v

View File

@ -13,7 +13,7 @@ import (
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
gdbmstorage "git.grassecon.net/urdt/ussd/internal/storage/db/gdbm"
gdbmstorage "git.grassecon.net/grassrootseconomics/visedriver/storage/db/gdbm"
)
var (
@ -59,7 +59,12 @@ func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.D
connStr := ms.conn.String()
dbTyp := ms.conn.DbType()
if dbTyp == DBTYPE_POSTGRES {
newDb = postgres.NewPgDb()
// TODO: move to vise
err = ensureSchemaExists(ctx, ms.conn)
if err != nil {
return nil, err
}
newDb = postgres.NewPgDb().WithSchema(ms.conn.Domain())
} else if dbTyp == DBTYPE_GDBM {
err = ms.ensureDbDir()
if err != nil {
@ -70,7 +75,7 @@ func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.D
} else {
return nil, fmt.Errorf("unsupported connection string: '%s'\n", ms.conn.String())
}
logg.DebugCtxf(ctx, "connecting to db", "conn", connStr)
logg.DebugCtxf(ctx, "connecting to db", "conn", connStr, "conndata", ms.conn)
err = newDb.Connect(ctx, connStr)
if err != nil {
return nil, err
@ -101,6 +106,23 @@ func (ms *MenuStorageService) WithGettext(path string, lns []lang.Language) *Men
return ms
}
// ensureSchemaExists creates a new schema if it does not exist
func ensureSchemaExists(ctx context.Context, conn ConnData) error {
h, err := pgxpool.New(ctx, conn.Path())
if err != nil {
return fmt.Errorf("failed to connect to the database: %w", err)
}
defer h.Close()
query := fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", conn.Domain())
_, err = h.Exec(ctx, query)
if err != nil {
return fmt.Errorf("failed to create schema: %w", err)
}
return nil
}
func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) {
stateStore, err := ms.GetStateStore(ctx)
if err != nil {

209
testutil/engine.go Normal file
View File

@ -0,0 +1,209 @@
package testutil
import (
"context"
"fmt"
"log"
"net/url"
"os"
"path"
"path/filepath"
"time"
"github.com/jackc/pgx/v5/pgxpool"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/visedriver/initializers"
"git.grassecon.net/grassrootseconomics/visedriver/config"
"git.grassecon.net/grassrootseconomics/visedriver/handlers"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"git.grassecon.net/grassrootseconomics/visedriver/internal/testutil/testservice"
"git.grassecon.net/grassrootseconomics/visedriver/internal/testutil/testtag"
testdataloader "github.com/peteole/testdata-loader"
"git.grassecon.net/grassrootseconomics/visedriver/remote"
)
var (
logg = logging.NewVanilla()
baseDir = testdataloader.GetBasePath()
scriptDir = path.Join(baseDir, "services", "registration")
setDbType string
setConnStr string
setDbSchema string
)
func init() {
initializers.LoadEnvVariablesPath(baseDir)
config.LoadConfig()
}
// SetDatabase updates the database used by TestEngine
func SetDatabase(database, connStr, dbSchema string) {
setDbType = database
setConnStr = connStr
setDbSchema = dbSchema
}
// CleanDatabase removes all test data from the database
func CleanDatabase() {
if setDbType == "postgres" {
ctx := context.Background()
// Update the connection string with the new search path
updatedConnStr, err := updateSearchPath(setConnStr, setDbSchema)
if err != nil {
log.Fatalf("Failed to update search path: %v", err)
}
dbConn, err := pgxpool.New(ctx, updatedConnStr)
if err != nil {
log.Fatalf("Failed to connect to database for cleanup: %v", err)
}
defer dbConn.Close()
query := fmt.Sprintf("DELETE FROM %s.kv_vise;", setDbSchema)
_, execErr := dbConn.Exec(ctx, query)
if execErr != nil {
log.Printf("Failed to cleanup table %s.kv_vise: %v", setDbSchema, execErr)
} else {
log.Printf("Successfully cleaned up table %s.kv_vise", setDbSchema)
}
} else {
setConnStr, _ := filepath.Abs(setConnStr)
if err := os.RemoveAll(setConnStr); err != nil {
log.Fatalf("Failed to delete state store %s: %v", setConnStr, err)
}
}
}
// updateSearchPath updates the search_path (schema) to be used in the connection
func updateSearchPath(connStr string, newSearchPath string) (string, error) {
u, err := url.Parse(connStr)
if err != nil {
return "", fmt.Errorf("invalid connection string: %w", err)
}
// Parse the query parameters
q := u.Query()
// Update or add the search_path parameter
q.Set("search_path", newSearchPath)
// Rebuild the connection string with updated parameters
u.RawQuery = q.Encode()
return u.String(), nil
}
func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
var err error
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId)
pfp := path.Join(scriptDir, "pp.csv")
var eventChannel = make(chan bool)
cfg := engine.Config{
Root: "root",
SessionId: sessionId,
OutputSize: uint32(160),
FlagCount: uint32(128),
}
if setDbType == "postgres" {
setConnStr = config.DbConn
setConnStr, err = updateSearchPath(setConnStr, setDbSchema)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
} else {
setConnStr, err = filepath.Abs(setConnStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
os.Exit(1)
}
}
conn, err := storage.ToConnData(setConnStr)
if err != nil {
fmt.Fprintf(os.Stderr, "connstr parse err: %v", err)
os.Exit(1)
}
resourceDir := scriptDir
menuStorageService := storage.NewMenuStorageService(conn, resourceDir)
rs, err := menuStorageService.GetResource(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "resource error: %v", err)
os.Exit(1)
}
pe, err := menuStorageService.GetPersister(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "persister error: %v", err)
os.Exit(1)
}
userDataStore, err := menuStorageService.GetUserdataDb(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "userdb error: %v", err)
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
fmt.Fprintf(os.Stderr, "dbresource cast error")
os.Exit(1)
}
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userDataStore)
lhs.SetPersister(pe)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
if testtag.AccountService == nil {
testtag.AccountService = &remote.AccountService{}
}
switch testtag.AccountService.(type) {
case *testservice.TestAccountService:
go func() {
eventChannel <- false
}()
case *remote.AccountService:
go func() {
time.Sleep(5 * time.Second) // Wait for 5 seconds
eventChannel <- true
}()
default:
panic("Unknown account service type")
}
hl, err := lhs.GetHandler(testtag.AccountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
en := lhs.GetEngine()
en = en.WithFirst(hl.Init)
cleanFn := func() {
err := en.Finish()
if err != nil {
logg.Errorf(err.Error())
}
err = menuStorageService.Close()
if err != nil {
logg.Errorf(err.Error())
}
logg.Infof("testengine storage closed")
}
return en, cleanFn, eventChannel
}

View File

@ -4,7 +4,7 @@ import (
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/request"
"git.grassecon.net/grassrootseconomics/visedriver/request"
)
// MockRequestHandler implements request.RequestHandler interface for testing

View File

@ -4,7 +4,7 @@ import (
"context"
"encoding/json"
"git.grassecon.net/urdt/ussd/models"
"git.grassecon.net/grassrootseconomics/visedriver/models"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)

View File

@ -0,0 +1,12 @@
// +build !online
package testtag
import (
"git.grassecon.net/grassrootseconomics/visedriver/remote"
accountservice "git.grassecon.net/grassrootseconomics/visedriver/internal/testutil/testservice"
)
var (
AccountService remote.AccountServiceInterface = &accountservice.TestAccountService{}
)

View File

@ -2,7 +2,7 @@
package testtag
import "git.grassecon.net/urdt/ussd/remote"
import "git.grassecon.net/grassrootseconomics/visedriver/remote"
var (
AccountService remote.AccountServiceInterface