Compare commits

..

63 Commits

Author SHA1 Message Date
alfred-mk
476a69fe1b Update menuhandler tests to use the memdb instead of dbmocks 2024-11-02 03:22:33 +03:00
alfred-mk
c52f8312e4 Remove the dbmock and userdbmock 2024-11-02 03:21:38 +03:00
alfred-mk
cfe3e526df Remove the subprefixdbmock 2024-11-01 13:17:45 +03:00
45945ae9c5 Merge pull request 'Consolidate temp data storage' (#150) from consolidate-temp-data-storage into master
Reviewed-on: #150
2024-10-31 23:47:04 +01:00
alfred-mk
d7ea8fa651 Use the DATA_TEMPORARY_VALUE for the user data 2024-11-01 01:10:58 +03:00
alfred-mk
e75aa2c1f7 Merge branch 'master' into consolidate-temp-data-storage 2024-11-01 00:45:32 +03:00
alfred-mk
63eed81d3d Merge branch 'master' into consolidate-temp-data-storage 2024-11-01 00:44:15 +03:00
alfred-mk
f4ca4454ea use a single DATA_TEMPORARY_VALUE for the PIN and voucher data 2024-11-01 00:42:23 +03:00
5f666382ab Merge pull request 'profile-update-pin-check' (#149) from profile-update-pin-check into master
Reviewed-on: #149
2024-10-31 16:06:47 +01:00
Carlosokumu
ce917d9e89 update tests 2024-10-31 17:05:06 +03:00
Carlosokumu
3ce25d0e14 Merge branch 'master' into profile-update-pin-check 2024-10-31 16:42:30 +03:00
alfred-mk
0b4bf58107 resolved failing tests due to tempData 2024-10-31 16:31:29 +03:00
074345fcf9 Merge pull request 'voucher-data' (#138) from voucher-data into master
Reviewed-on: #138
2024-10-31 13:44:18 +01:00
alfred-mk
e6a369dcdd remove unused code 2024-10-31 14:44:42 +03:00
alfred-mk
b8bbd88078 retain the temporary data for it to be overwritten 2024-10-31 14:26:28 +03:00
Carlosokumu
d25128287e update profile information on individual node 2024-10-31 14:21:22 +03:00
Carlosokumu
c45fcda2f1 remove back option check,protect profile data update 2024-10-31 14:20:04 +03:00
Carlosokumu
211cc1f775 perform profile update in individual update node 2024-10-31 14:19:12 +03:00
Carlosokumu
981f7ca4f6 add keys to for holding temporary profile info 2024-10-31 14:14:50 +03:00
Carlosokumu
05ed236e03 add node to update individual profile information 2024-10-31 14:14:10 +03:00
alfred-mk
a31cac4e50 Merge branch 'master' into voucher-data 2024-10-30 18:46:21 +03:00
alfred-mk
8b399781e8 make the VoucherMetadata more descriptive 2024-10-30 18:40:03 +03:00
alfred-mk
caafe495be use the dataserviceapi structs 2024-10-30 18:30:55 +03:00
alfred-mk
66b34eaea4 get the ussd-data-service package 2024-10-30 18:21:49 +03:00
alfred-mk
adaa0c63ef Extract common db operations for the test 2024-10-30 14:12:42 +03:00
carlos
843b0d1e7e Merge pull request 'Add reverse sessionid address lookup' (#146) from lash/reverse-session into master
Reviewed-on: #146
2024-10-30 10:35:55 +01:00
lash
cb997159f7 Add reverse sessionid address lookup 2024-10-30 00:59:59 +00:00
alfred-mk
7241cdbfcb Updated the tests 2024-10-30 00:53:13 +03:00
alfred-mk
0480c02633 Moved voucher related functions to the utils package 2024-10-30 00:52:23 +03:00
alfred-mk
03c32fd265 Merge branch 'master' into voucher-data 2024-10-28 18:40:37 +03:00
727f54ee57 Merge pull request 'tests-refactor' (#137) from tests-refactor into master
Reviewed-on: #137
2024-10-28 16:28:41 +01:00
alfred-mk
2c361e5b96 Added tests related to the vouchers list 2024-10-28 16:30:13 +03:00
alfred-mk
131f3bcf46 Added the prefixDb to the Handlers struct~ 2024-10-28 16:27:24 +03:00
alfred-mk
520f5abdcd Added the subPrefixDb mock 2024-10-28 16:23:44 +03:00
alfred-mk
5d294b663c Added the PrefixDb interface 2024-10-28 16:21:31 +03:00
alfred-mk
dc782d87a8 Updated the TestSetVoucher 2024-10-28 11:20:06 +03:00
alfred-mk
ddae746b9d formatted code 2024-10-28 11:07:59 +03:00
Carlosokumu
4e1b2d5ddb update account services 2024-10-25 18:01:52 +03:00
Carlosokumu
d8800a665d Merge branch 'master' into tests-refactor 2024-10-25 17:48:40 +03:00
alfred-mk
6c3ff0e9db Merge branch 'master' into voucher-data 2024-10-25 17:37:08 +03:00
alfred-mk
3ef64271e7 Store and retrieve the voucher decimals and contract address 2024-10-25 17:24:09 +03:00
Carlosokumu
3792bfdc1f run mod tidy 2024-10-25 09:38:09 +03:00
Carlosokumu
a0e97cfe5b remove test account service implementation 2024-10-25 09:37:16 +03:00
Carlosokumu
a3be98c514 update test account service 2024-10-25 09:36:43 +03:00
Carlosokumu
2b34b3a75c Merge branch 'master' into tests-refactor 2024-10-25 09:14:16 +03:00
Carlosokumu
f99486c190 correct import 2024-10-24 12:07:00 +03:00
Carlosokumu
57a07af8ca correct import 2024-10-24 12:06:44 +03:00
Carlosokumu
5692440099 move to testutil 2024-10-24 12:05:27 +03:00
Carlosokumu
651969668f move to testutil 2024-10-24 12:05:11 +03:00
Carlosokumu
f3a028f1fc move to testutil 2024-10-24 12:03:32 +03:00
Carlosokumu
1e6cf6a33a run mod tidy 2024-10-23 11:08:51 +03:00
Carlosokumu
2725323f16 Merge branch 'master' into tests-refactor 2024-10-23 11:06:10 +03:00
Carlosokumu
306666a15e load and reload after halt 2024-10-22 20:37:30 +03:00
Carlosokumu
e05dbb4885 check for back option 2024-10-22 20:36:58 +03:00
Carlosokumu
eaac771722 move into testtag package 2024-10-21 16:48:24 +03:00
Carlosokumu
f3a5178de7 update imports 2024-10-21 16:47:43 +03:00
Carlosokumu
549d09890b update imports 2024-10-21 16:47:25 +03:00
Carlosokumu
4a9ef6b5f2 move test account service to testutil 2024-10-21 16:47:00 +03:00
Carlosokumu
961d8d1a5c update package import 2024-10-21 16:46:29 +03:00
Carlosokumu
a904cdbf44 move driver to testutil 2024-10-21 16:45:06 +03:00
Carlosokumu
3af943f77c move account service tags switch to test tags package 2024-10-21 16:44:02 +03:00
Carlosokumu
14455f65cb move account service to testutil 2024-10-21 16:43:24 +03:00
Carlosokumu
0c08654df3 move driver to testutil 2024-10-21 16:42:57 +03:00
41 changed files with 1352 additions and 987 deletions

18
common/hex.go Normal file
View File

@@ -0,0 +1,18 @@
package common
import (
"encoding/hex"
)
func NormalizeHex(s string) (string, error) {
if len(s) >= 2 {
if s[:2] == "0x" {
s = s[2:]
}
}
r, err := hex.DecodeString(s)
if err != nil {
return "", err
}
return hex.EncodeToString(r), nil
}

9
go.mod
View File

@@ -7,19 +7,19 @@ toolchain go1.23.2
require (
git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b
github.com/alecthomas/assert/v2 v2.2.2
github.com/grassrootseconomics/eth-custodial v1.3.0-beta
github.com/peteole/testdata-loader v0.3.0
gopkg.in/leonelquinteros/gotext.v1 v1.3.1
)
require github.com/joho/godotenv v1.5.1
require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect
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/joho/godotenv v1.5.1
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
@@ -42,7 +42,4 @@ require (
github.com/stretchr/testify v1.9.0
github.com/x448/float16 v0.8.4 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

2
go.sum
View File

@@ -18,6 +18,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1
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/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a h1:q/YH7nE2j8epNmFnTu0tU1vwtCxtQ6nH+d7hRVV5krU=
github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a/go.mod h1:hdKaKwqiW6/kphK4j/BhmuRlZDLo1+DYo3gYw5O0siw=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=

View File

@@ -8,7 +8,6 @@ import (
"io"
"net/http"
"os"
"time"
"git.grassecon.net/urdt/ussd/config"
"git.grassecon.net/urdt/ussd/internal/models"
@@ -31,9 +30,6 @@ type AccountServiceInterface interface {
type AccountService struct {
}
type TestAccountService struct {
}
// Parameters:
// - 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
@@ -187,88 +183,3 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) (
}
return &holdings, nil
}
func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) {
return &api.OKResponse{
Ok: true,
Description: "Account creation request received successfully",
Result: map[string]any{"publicKey": "0x48ADca309b5085852207FAaf2816eD72B52F527C", "trackingId": "28ebe84d-b925-472c-87ae-bbdfa1fb97be"},
}, nil
}
func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) {
balanceResponse := &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
}
return balanceResponse, nil
}
func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) {
return &api.OKResponse{
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
}
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) {
return &models.VoucherHoldingResponse{
Ok: true,
Result: struct {
Holdings []struct {
ContractAddress string `json:"contractAddress"`
TokenSymbol string `json:"tokenSymbol"`
TokenDecimals string `json:"tokenDecimals"`
Balance string `json:"balance"`
} `json:"holdings"`
}{
Holdings: []struct {
ContractAddress string `json:"contractAddress"`
TokenSymbol string `json:"tokenSymbol"`
TokenDecimals string `json:"tokenDecimals"`
Balance string `json:"balance"`
}{
{
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
TokenSymbol: "SRF",
TokenDecimals: "6",
Balance: "2745987",
},
},
},
}, nil
}

View File

@@ -19,6 +19,7 @@ import (
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state"
"git.grassecon.net/urdt/ussd/common"
"git.grassecon.net/urdt/ussd/internal/handlers/server"
"git.grassecon.net/urdt/ussd/internal/utils"
"gopkg.in/leonelquinteros/gotext.v1"
@@ -64,6 +65,7 @@ type Handlers struct {
userdataStore utils.DataStore
flagManager *asm.FlagParser
accountService server.AccountServiceInterface
prefixDb storage.PrefixDb
}
func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) {
@@ -73,10 +75,14 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s
userDb := &utils.UserDataStore{
Db: userdataStore,
}
// Instantiate the SubPrefixDb with "vouchers" prefix
prefixDb := storage.NewSubPrefixDb(userdataStore, []byte("vouchers"))
h := &Handlers{
userdataStore: userDb,
flagManager: appFlags,
accountService: accountService,
prefixDb: prefixDb,
}
return h, nil
}
@@ -152,15 +158,22 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r
data := map[utils.DataTyp]string{
utils.DATA_TRACKING_ID: trackingId,
utils.DATA_PUBLIC_KEY: publicKey,
utils.DATA_ADDRESS: sessionId,
}
store := h.userdataStore
for key, value := range data {
store := h.userdataStore
err := store.WriteEntry(ctx, sessionId, key, []byte(value))
err = store.WriteEntry(ctx, sessionId, key, []byte(value))
if err != nil {
return err
}
}
publicKeyNormalized, err := common.NormalizeHex(publicKey)
if err != nil {
return err
}
err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId))
if err != nil {
return err
}
res.FlagSet = append(res.FlagSet, flag_account_created)
return nil
@@ -208,7 +221,7 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (
return res, nil
}
// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_PIN
// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE
// during the account creation process
// and during the change PIN process
func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
@@ -233,7 +246,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN))
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN))
if err != nil {
return res, err
}
@@ -250,7 +263,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
store := h.userdataStore
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN)
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
if err != nil {
return res, err
}
@@ -281,7 +294,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte
return res, fmt.Errorf("missing session")
}
store := h.userdataStore
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN)
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
if err != nil {
return res, err
}
@@ -320,10 +333,18 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte)
if !ok {
return res, fmt.Errorf("missing session")
}
if len(input) > 0 {
firstName := string(input)
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName))
firstName := string(input)
store := h.userdataStore
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
if allowUpdate {
temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName))
if err != nil {
return res, err
}
} else {
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName))
if err != nil {
return res, err
}
@@ -340,18 +361,24 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte)
if !ok {
return res, fmt.Errorf("missing session")
}
store := h.userdataStore
familyName := string(input)
if len(input) > 0 {
familyName := string(input)
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName))
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
if allowUpdate {
temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName))
if err != nil {
return res, err
}
} else {
return res, fmt.Errorf("a family name cannot be less than one character")
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName))
if err != nil {
return res, err
}
}
return res, nil
}
@@ -363,10 +390,19 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou
if !ok {
return res, fmt.Errorf("missing session")
}
if len(input) == 4 {
yob := string(input)
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob))
yob := string(input)
store := h.userdataStore
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
if allowUpdate {
temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob))
if err != nil {
return res, err
}
} else {
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob))
if err != nil {
return res, err
}
@@ -383,11 +419,20 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) (
if !ok {
return res, fmt.Errorf("missing session")
}
location := string(input)
store := h.userdataStore
if len(input) > 0 {
location := string(input)
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location))
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
if allowUpdate {
temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation))
if err != nil {
return res, err
}
} else {
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location))
if err != nil {
return res, err
}
@@ -407,9 +452,20 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re
}
gender := strings.Split(symbol, "_")[1]
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender))
if err != nil {
return res, nil
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
if allowUpdate {
temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender))
if err != nil {
return res, err
}
} else {
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender))
if err != nil {
return res, err
}
}
return res, nil
@@ -423,12 +479,22 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte)
if !ok {
return res, fmt.Errorf("missing session")
}
if len(input) > 0 {
offerings := string(input)
store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings))
offerings := string(input)
store := h.userdataStore
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
if allowUpdate {
temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings))
if err != nil {
return res, nil
return res, err
}
} else {
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings))
if err != nil {
return res, err
}
}
@@ -533,7 +599,6 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b
if err != nil {
return res, err
}
okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
@@ -1033,13 +1098,13 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by
if db.IsNotFound(err) {
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
if err != nil {
return res, nil
return res, err
}
// Fetch vouchers from the API using the public key
vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey))
if err != nil {
return res, nil
return res, err
}
// Return if there is no voucher
@@ -1096,54 +1161,33 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
return res, nil
}
// process voucher data
voucherSymbolList, voucherBalanceList := ProcessVouchers(vouchersResp.Result.Holdings)
data := utils.ProcessVouchers(vouchersResp.Result.Holdings)
prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers"))
err = prefixdb.Put(ctx, []byte("sym"), []byte(voucherSymbolList))
if err != nil {
return res, nil
// Store all voucher data
dataMap := map[string]string{
"sym": data.Symbols,
"bal": data.Balances,
"deci": data.Decimals,
"addr": data.Addresses,
}
err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList))
if err != nil {
return res, nil
for key, value := range dataMap {
if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil {
return res, nil
}
}
return res, nil
}
// ProcessVouchers formats the holdings into symbol and balance lists.
func ProcessVouchers(holdings []struct {
ContractAddress string `json:"contractAddress"`
TokenSymbol string `json:"tokenSymbol"`
TokenDecimals string `json:"tokenDecimals"`
Balance string `json:"balance"`
}) (string, string) {
var numberedSymbols, numberedBalances []string
for i, voucher := range holdings {
numberedSymbols = append(numberedSymbols, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol))
numberedBalances = append(numberedBalances, fmt.Sprintf("%d:%s", i+1, voucher.Balance))
}
voucherSymbolList := strings.Join(numberedSymbols, "\n")
voucherBalanceList := strings.Join(numberedBalances, "\n")
return voucherSymbolList, voucherBalanceList
}
// GetVoucherList fetches the list of vouchers and formats them
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
// Read vouchers from the store
store := h.userdataStore
prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers"))
voucherData, err := prefixdb.Get(ctx, []byte("sym"))
voucherData, err := h.prefixDb.Get(ctx, []byte("sym"))
if err != nil {
return res, nil
return res, err
}
res.Content = string(voucherData)
@@ -1154,8 +1198,6 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte)
// ViewVoucher retrieves the token holding and balance from the subprefixDB
func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
@@ -1164,126 +1206,51 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
inputStr := string(input)
if inputStr == "0" || inputStr == "99" {
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
return res, nil
}
prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers"))
// Retrieve the voucher symbol list
voucherSymbolList, err := prefixdb.Get(ctx, []byte("sym"))
metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr)
if err != nil {
return res, fmt.Errorf("failed to retrieve voucher symbol list: %v", err)
return res, fmt.Errorf("failed to retrieve voucher data: %v", err)
}
// Retrieve the voucher balance list
voucherBalanceList, err := prefixdb.Get(ctx, []byte("bal"))
if err != nil {
return res, fmt.Errorf("failed to retrieve voucher balance list: %v", err)
}
// match the voucher symbol and balance with the input
matchedSymbol, matchedBalance := MatchVoucher(inputStr, string(voucherSymbolList), string(voucherBalanceList))
// If a match is found, write the temporary sym and balance
if matchedSymbol != "" && matchedBalance != "" {
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol))
if err != nil {
return res, err
}
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance))
if err != nil {
return res, err
}
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance)
} else {
if metadata == nil {
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
return res, nil
}
if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
return res, err
}
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
res.Content = fmt.Sprintf("%s\n%s", metadata.TokenSymbol, metadata.Balance)
return res, nil
}
// MatchVoucher finds the matching voucher symbol and balance based on the input.
func MatchVoucher(inputStr string, voucherSymbols, voucherBalances string) (string, string) {
// Split the lists into slices for processing
symbols := strings.Split(voucherSymbols, "\n")
balances := strings.Split(voucherBalances, "\n")
var matchedSymbol, matchedBalance string
for i, symbol := range symbols {
symbolParts := strings.SplitN(symbol, ":", 2)
if len(symbolParts) != 2 {
continue
}
voucherNum := symbolParts[0]
voucherSymbol := symbolParts[1]
// Check if input matches either the number or the symbol
if inputStr == voucherNum || strings.EqualFold(inputStr, voucherSymbol) {
matchedSymbol = voucherSymbol
// Ensure there's a corresponding balance
if i < len(balances) {
matchedBalance = strings.SplitN(balances[i], ":", 2)[1]
}
break
}
}
return matchedSymbol, matchedBalance
}
// SetVoucher retrieves the temporary sym and balance,
// sets them as the active data and
// clears the temporary data
// SetVoucher retrieves the temp voucher data and sets it as the active data
func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
// get the current temporary symbol
temporarySym, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM)
if err != nil {
return res, err
}
// get the current temporary balance
temporaryBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL)
// Get temporary data
tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
if err != nil {
return res, err
}
// set the active symbol
err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym))
if err != nil {
return res, err
}
// set the active balance
err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(temporaryBal))
if err != nil {
// Set as active and clear temporary data
if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil {
return res, err
}
// reset the temporary symbol
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(""))
if err != nil {
return res, err
}
// reset the temporary balance
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(""))
if err != nil {
return res, err
}
res.Content = string(temporarySym)
res.Content = tempData.TokenSymbol
return res, nil
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,59 +0,0 @@
package mocks
import (
"context"
"git.defalsify.org/vise.git/lang"
"github.com/stretchr/testify/mock"
)
type MockDb struct {
mock.Mock
}
func (m *MockDb) SetPrefix(prefix uint8) {
m.Called(prefix)
}
func (m *MockDb) Prefix() uint8 {
args := m.Called()
return args.Get(0).(uint8)
}
func (m *MockDb) Safe() bool {
args := m.Called()
return args.Get(0).(bool)
}
func (m *MockDb) SetLanguage(language *lang.Language) {
m.Called(language)
}
func (m *MockDb) SetLock(uint8, bool) error {
args := m.Called()
return args.Error(0)
}
func (m *MockDb) Connect(ctx context.Context, connectionStr string) error {
args := m.Called(ctx, connectionStr)
return args.Error(0)
}
func (m *MockDb) SetSession(sessionId string) {
m.Called(sessionId)
}
func (m *MockDb) Put(ctx context.Context, key, value []byte) error {
args := m.Called(ctx, key, value)
return args.Error(0)
}
func (m *MockDb) Get(ctx context.Context, key []byte) ([]byte, error) {
args := m.Called(ctx, key)
return nil, args.Error(0)
}
func (m *MockDb) Close() error {
args := m.Called(nil)
return args.Error(0)
}

View File

@@ -1,24 +0,0 @@
package mocks
import (
"context"
"git.defalsify.org/vise.git/db"
"git.grassecon.net/urdt/ussd/internal/utils"
"github.com/stretchr/testify/mock"
)
type MockUserDataStore struct {
db.Db
mock.Mock
}
func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) {
args := m.Called(ctx, sessionId, typ)
return args.Get(0).([]byte), args.Error(1)
}
func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ utils.DataTyp, value []byte) error {
args := m.Called(ctx, sessionId, typ, value)
return args.Error(0)
}

View File

@@ -1,15 +1,14 @@
package models
// VoucherHoldingResponse represents a single voucher holding
import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
type VoucherHoldingResponse struct {
Ok bool `json:"ok"`
Description string `json:"description"`
Result struct {
Holdings []struct {
ContractAddress string `json:"contractAddress"`
TokenSymbol string `json:"tokenSymbol"`
TokenDecimals string `json:"tokenDecimals"`
Balance string `json:"balance"`
} `json:"holdings"`
} `json:"result"`
Ok bool `json:"ok"`
Description string `json:"description"`
Result VoucherResult `json:"result"`
}
// VoucherResult holds the list of token holdings
type VoucherResult struct {
Holdings []dataserviceapi.TokenHoldings `json:"holdings"`
}

View File

@@ -10,6 +10,14 @@ const (
DATATYPE_USERSUB = 64
)
// PrefixDb interface abstracts the database operations.
type PrefixDb interface {
Get(ctx context.Context, key []byte) ([]byte, error)
Put(ctx context.Context, key []byte, val []byte) error
}
var _ PrefixDb = (*SubPrefixDb)(nil)
type SubPrefixDb struct {
store db.Db
pfx []byte
@@ -29,11 +37,7 @@ func (s *SubPrefixDb) toKey(k []byte) []byte {
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
return s.store.Get(ctx, key)
}
func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error {

View File

@@ -13,6 +13,8 @@ import (
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/server"
"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"
)
@@ -80,12 +82,12 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
os.Exit(1)
}
if AccountService == nil {
AccountService = &server.AccountService{}
if testtag.AccountService == nil {
testtag.AccountService = &server.AccountService{}
}
switch AccountService.(type) {
case *server.TestAccountService:
switch testtag.AccountService.(type) {
case *testservice.TestAccountService:
go func() {
eventChannel <- false
}()
@@ -98,7 +100,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
panic("Unknown account service type")
}
hl, err := lhs.GetHandler(AccountService)
hl, err := lhs.GetHandler(testtag.AccountService)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)

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

@@ -0,0 +1,90 @@
package testservice
import (
"context"
"encoding/json"
"time"
"git.grassecon.net/urdt/ussd/internal/models"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
type TestAccountService struct {
}
func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) {
return &api.OKResponse{
Ok: true,
Description: "Account creation succeeded",
Result: map[string]any{
"trackingId": "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d",
"publicKey": "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD",
},
}, nil
}
func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) {
balanceResponse := &models.BalanceResponse{
Ok: true,
Result: struct {
Balance string `json:"balance"`
Nonce json.Number `json:"nonce"`
}{
Balance: "0.003 CELO",
Nonce: json.Number("0"),
},
}
return balanceResponse, nil
}
func (tas *TestAccountService) CheckAccountStatus(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
}
func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) {
return &api.OKResponse{
Ok: true,
Description: "Account creation succeeded",
Result: map[string]any{
"active": true,
},
}, nil
}
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) {
return &models.VoucherHoldingResponse{
Ok: true,
Result: models.VoucherResult{
Holdings: []dataserviceapi.TokenHoldings{
{
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
TokenSymbol: "SRF",
TokenDecimals: "6",
Balance: "2745987",
},
},
},
}, nil
}

View File

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

View File

@@ -1,6 +1,6 @@
// +build online
package testutil
package testtag
import "git.grassecon.net/urdt/ussd/internal/handlers/server"

View File

@@ -22,13 +22,14 @@ const (
DATA_OFFERINGS
DATA_RECIPIENT
DATA_AMOUNT
DATA_TEMPORARY_PIN
DATA_TEMPORARY_VALUE
DATA_VOUCHER_LIST
DATA_TEMPORARY_SYM
DATA_ACTIVE_SYM
DATA_TEMPORARY_BAL
DATA_ACTIVE_BAL
DATA_ADDRESS
DATA_PUBLIC_KEY_REVERSE
DATA_ACTIVE_DECIMAL
DATA_ACTIVE_ADDRESS
)
func typToBytes(typ DataTyp) []byte {

146
internal/utils/vouchers.go Normal file
View File

@@ -0,0 +1,146 @@
package utils
import (
"context"
"fmt"
"strings"
"git.grassecon.net/urdt/ussd/internal/storage"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
// VoucherMetadata helps organize data fields
type VoucherMetadata struct {
Symbols string
Balances string
Decimals string
Addresses string
}
// ProcessVouchers converts holdings into formatted strings
func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata {
var data VoucherMetadata
var symbols, balances, decimals, addresses []string
for i, h := range holdings {
symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol))
balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance))
decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals))
addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress))
}
data.Symbols = strings.Join(symbols, "\n")
data.Balances = strings.Join(balances, "\n")
data.Decimals = strings.Join(decimals, "\n")
data.Addresses = strings.Join(addresses, "\n")
return data
}
// GetVoucherData retrieves and matches voucher data
func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) {
keys := []string{"sym", "bal", "deci", "addr"}
data := make(map[string]string)
for _, key := range keys {
value, err := db.Get(ctx, []byte(key))
if err != nil {
return nil, fmt.Errorf("failed to get %s: %v", key, err)
}
data[key] = string(value)
}
symbol, balance, decimal, address := MatchVoucher(input,
data["sym"],
data["bal"],
data["deci"],
data["addr"])
if symbol == "" {
return nil, nil
}
return &dataserviceapi.TokenHoldings{
TokenSymbol: string(symbol),
Balance: string(balance),
TokenDecimals: string(decimal),
ContractAddress: string(address),
}, nil
}
// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input.
func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) {
symList := strings.Split(symbols, "\n")
balList := strings.Split(balances, "\n")
decList := strings.Split(decimals, "\n")
addrList := strings.Split(addresses, "\n")
for i, sym := range symList {
parts := strings.SplitN(sym, ":", 2)
if input == parts[0] || strings.EqualFold(input, parts[1]) {
symbol = parts[1]
if i < len(balList) {
balance = strings.SplitN(balList[i], ":", 2)[1]
}
if i < len(decList) {
decimal = strings.SplitN(decList[i], ":", 2)[1]
}
if i < len(addrList) {
address = strings.SplitN(addrList[i], ":", 2)[1]
}
break
}
}
return
}
// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore.
func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error {
tempData := fmt.Sprintf("%s,%s,%s,%s", data.TokenSymbol, data.Balance, data.TokenDecimals, data.ContractAddress)
if err := store.WriteEntry(ctx, sessionId, DATA_TEMPORARY_VALUE, []byte(tempData)); err != nil {
return err
}
return nil
}
// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore.
func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) {
temp_data, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE)
if err != nil {
return nil, err
}
values := strings.SplitN(string(temp_data), ",", 4)
data := &dataserviceapi.TokenHoldings{}
data.TokenSymbol = values[0]
data.Balance = values[1]
data.TokenDecimals = values[2]
data.ContractAddress = values[3]
return data, nil
}
// UpdateVoucherData sets the active voucher data in the DataStore.
func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error {
// Active voucher data entries
activeEntries := map[DataTyp][]byte{
DATA_ACTIVE_SYM: []byte(data.TokenSymbol),
DATA_ACTIVE_BAL: []byte(data.Balance),
DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals),
DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress),
}
// Write active data
for key, value := range activeEntries {
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,198 @@
package utils
import (
"context"
"fmt"
"testing"
"git.grassecon.net/urdt/ussd/internal/storage"
"github.com/alecthomas/assert/v2"
"github.com/stretchr/testify/require"
memdb "git.defalsify.org/vise.git/db/mem"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
// InitializeTestDb sets up and returns an in-memory database and store.
func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) {
ctx := context.Background()
// Initialize memDb
db := memdb.NewMemDb()
err := db.Connect(ctx, "")
require.NoError(t, err, "Failed to connect to memDb")
// Create UserDataStore with memDb
store := &UserDataStore{Db: db}
t.Cleanup(func() {
db.Close() // Ensure the DB is closed after each test
})
return ctx, store
}
func TestMatchVoucher(t *testing.T) {
symbols := "1:SRF\n2:MILO"
balances := "1:100\n2:200"
decimals := "1:6\n2:4"
addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa"
// Test for valid voucher
symbol, balance, decimal, address := MatchVoucher("2", symbols, balances, decimals, addresses)
// Assertions for valid voucher
assert.Equal(t, "MILO", symbol)
assert.Equal(t, "200", balance)
assert.Equal(t, "4", decimal)
assert.Equal(t, "0x41c188d63Qa", address)
// Test for non-existent voucher
symbol, balance, decimal, address = MatchVoucher("3", symbols, balances, decimals, addresses)
// Assertions for non-match
assert.Equal(t, "", symbol)
assert.Equal(t, "", balance)
assert.Equal(t, "", decimal)
assert.Equal(t, "", address)
}
func TestProcessVouchers(t *testing.T) {
holdings := []dataserviceapi.TokenHoldings{
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
}
expectedResult := VoucherMetadata{
Symbols: "1:SRF\n2:MILO",
Balances: "1:100\n2:200",
Decimals: "1:6\n2:4",
Addresses: "1:0xd4c288865Ce\n2:0x41c188d63Qa",
}
result := ProcessVouchers(holdings)
assert.Equal(t, expectedResult, result)
}
func TestGetVoucherData(t *testing.T) {
ctx := context.Background()
db := memdb.NewMemDb()
err := db.Connect(ctx, "")
if err != nil {
t.Fatal(err)
}
spdb := storage.NewSubPrefixDb(db, []byte("vouchers"))
// Test voucher data
mockData := map[string][]byte{
"sym": []byte("1:SRF\n2:MILO"),
"bal": []byte("1:100\n2:200"),
"deci": []byte("1:6\n2:4"),
"addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"),
}
// Put the data
for key, value := range mockData {
err = spdb.Put(ctx, []byte(key), []byte(value))
if err != nil {
t.Fatal(err)
}
}
result, err := GetVoucherData(ctx, spdb, "1")
assert.NoError(t, err)
assert.Equal(t, "SRF", result.TokenSymbol)
assert.Equal(t, "100", result.Balance)
assert.Equal(t, "6", result.TokenDecimals)
assert.Equal(t, "0xd4c288865Ce", result.ContractAddress)
}
func TestStoreTemporaryVoucher(t *testing.T) {
ctx, store := InitializeTestDb(t)
sessionId := "session123"
// Test data
voucherData := &dataserviceapi.TokenHoldings{
TokenSymbol: "SRF",
Balance: "200",
TokenDecimals: "6",
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
}
// Execute the function being tested
err := StoreTemporaryVoucher(ctx, store, sessionId, voucherData)
require.NoError(t, err)
// Verify stored data
expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9")
storedValue, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE)
require.NoError(t, err)
require.Equal(t, expectedData, string(storedValue), "Mismatch for key %v", DATA_TEMPORARY_VALUE)
}
func TestGetTemporaryVoucherData(t *testing.T) {
ctx, store := InitializeTestDb(t)
sessionId := "session123"
// Test voucher data
tempData := &dataserviceapi.TokenHoldings{
TokenSymbol: "SRF",
Balance: "200",
TokenDecimals: "6",
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
}
// Store the data
err := StoreTemporaryVoucher(ctx, store, sessionId, tempData)
require.NoError(t, err)
// Execute the function being tested
data, err := GetTemporaryVoucherData(ctx, store, sessionId)
require.NoError(t, err)
require.Equal(t, tempData, data)
}
func TestUpdateVoucherData(t *testing.T) {
ctx, store := InitializeTestDb(t)
sessionId := "session123"
// New voucher data
newData := &dataserviceapi.TokenHoldings{
TokenSymbol: "SRF",
Balance: "200",
TokenDecimals: "6",
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
}
// Old temporary data
tempData := &dataserviceapi.TokenHoldings{
TokenSymbol: "OLD",
Balance: "100",
TokenDecimals: "8",
ContractAddress: "0xold",
}
require.NoError(t, StoreTemporaryVoucher(ctx, store, sessionId, tempData))
// Execute update
err := UpdateVoucherData(ctx, store, sessionId, newData)
require.NoError(t, err)
// Verify active data was stored correctly
activeEntries := map[DataTyp][]byte{
DATA_ACTIVE_SYM: []byte(newData.TokenSymbol),
DATA_ACTIVE_BAL: []byte(newData.Balance),
DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals),
DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress),
}
for key, expectedValue := range activeEntries {
storedValue, err := store.ReadEntry(ctx, sessionId, key)
require.NoError(t, err)
require.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key)
}
}

View File

@@ -9,8 +9,8 @@ import (
"regexp"
"testing"
"git.grassecon.net/urdt/ussd/driver"
"git.grassecon.net/urdt/ussd/internal/testutil"
"git.grassecon.net/urdt/ussd/internal/testutil/driver"
"github.com/gofrs/uuid"
)

View File

@@ -1,9 +1,8 @@
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
LOAD save_familyname 0
RELOAD save_familyname
CATCH update_familyname flag_allow_update 1
MOUT back 0
HALT
LOAD save_familyname 0
RELOAD save_familyname
INCMP _ 0
INCMP pin_entry *

View File

@@ -1,8 +1,8 @@
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
LOAD save_location 0
CATCH update_location flag_allow_update 1
MOUT back 0
HALT
LOAD save_location 0
RELOAD save_location
INCMP _ 0
INCMP pin_entry *

View File

@@ -1,12 +1,8 @@
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
LOAD save_firstname 0
RELOAD save_firstname
CATCH update_firstname flag_allow_update 1
MOUT back 0
HALT
LOAD save_firstname 0
RELOAD save_firstname
INCMP _ 0
INCMP pin_entry *

View File

@@ -1,5 +1,5 @@
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
CATCH update_offerings flag_allow_update 1
LOAD save_offerings 0
MOUT back 0
HALT

View File

@@ -1,10 +1,10 @@
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
LOAD save_yob 0
CATCH update_yob flag_allow_update 1
MOUT back 0
HALT
LOAD verify_yob 0
CATCH incorrect_date_format flag_incorrect_date_format 1
LOAD save_yob 0
RELOAD save_yob
INCMP _ 0
INCMP pin_entry *

View File

@@ -1,4 +1,4 @@
LOAD save_gender 0
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
CATCH update_gender flag_allow_update 1
MOVE pin_entry

View File

@@ -1,4 +1,4 @@
LOAD save_gender 0
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
CATCH update_gender flag_allow_update 1
MOVE pin_entry

View File

@@ -1,4 +1,4 @@
LOAD save_gender 0
CATCH incorrect_pin flag_incorrect_pin 1
CATCH profile_update_success flag_allow_update 1
CATCH update_gender flag_allow_update 1
MOVE pin_entry

View File

@@ -0,0 +1,2 @@
RELOAD save_yob
CATCH profile_update_success flag_allow_update 1

View File

@@ -0,0 +1,2 @@
RELOAD save_familyname
CATCH profile_update_success flag_allow_update 1

View File

@@ -0,0 +1,2 @@
RELOAD save_firstname
CATCH profile_update_success flag_allow_update 1

View File

@@ -0,0 +1,2 @@
RELOAD save_gender
CATCH profile_update_success flag_allow_update 1

View File

@@ -0,0 +1,2 @@
RELOAD save_location
CATCH profile_update_success flag_allow_update 1

View File

@@ -0,0 +1,2 @@
RELOAD save_offerings
CATCH profile_update_success flag_allow_update 1

View File

@@ -0,0 +1,2 @@
RELOAD save_yob
CATCH profile_update_success flag_allow_update 1