Compare commits

..

No commits in common. "master" and "remove-session-id-from-ctx" have entirely different histories.

76 changed files with 828 additions and 676 deletions

View File

@ -21,7 +21,8 @@ RUN make VISE_PATH=/build/go-vise -B
WORKDIR /build/sarafu-vise WORKDIR /build/sarafu-vise
RUN echo "Building on $BUILDPLATFORM, building for $TARGETPLATFORM" RUN echo "Building on $BUILDPLATFORM, building for $TARGETPLATFORM"
RUN go mod download RUN go mod download
RUN go build -tags logdebug,online -o sarafu-at -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go RUN go build -tags logtrace,online -o sarafu-at -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go
RUN go build -tags logtrace,online -o sarafu-ssh -ldflags="-X main.build=${BUILD} -s -w" cmd/ssh/main.go
FROM debian:bookworm-slim FROM debian:bookworm-slim
@ -36,6 +37,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /service WORKDIR /service
COPY --from=build /build/sarafu-vise/sarafu-at . COPY --from=build /build/sarafu-vise/sarafu-at .
COPY --from=build /build/sarafu-vise/sarafu-ssh .
COPY --from=build /build/sarafu-vise/LICENSE . COPY --from=build /build/sarafu-vise/LICENSE .
COPY --from=build /build/sarafu-vise/README.md . COPY --from=build /build/sarafu-vise/README.md .
COPY --from=build /build/sarafu-vise/services ./services COPY --from=build /build/sarafu-vise/services ./services
@ -43,5 +45,6 @@ COPY --from=build /build/sarafu-vise/.env.example .
RUN mv .env.example .env RUN mv .env.example .env
EXPOSE 7123 EXPOSE 7123
EXPOSE 7122
CMD ["./sarafu-at"] CMD ["./sarafu-at"]

6
go.mod
View File

@ -3,10 +3,10 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise
go 1.23.4 go 1.23.4
require ( require (
git.defalsify.org/vise.git v0.3.2-0.20250401123711-d481b04a6805 git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250411080608-34957e5b6ff8 git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2 git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694
github.com/alecthomas/assert/v2 v2.2.2 github.com/alecthomas/assert/v2 v2.2.2
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid v4.4.0+incompatible

22
go.sum
View File

@ -1,21 +1,11 @@
git.defalsify.org/vise.git v0.3.1 h1:A6FhMcur09ft/JzUPGXR+KpA17fltfeBnasyvLMZmq4= git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac h1:f/E0ZTclVfMEnD/3Alrzzbg+dOm138zGydV42jT0JPw=
git.defalsify.org/vise.git v0.3.1/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.defalsify.org/vise.git v0.3.2-0.20250401123711-d481b04a6805 h1:FnT39aqXcP5YWhwPDBABopSjCu2SlbPFoOVitSpAVxU=
git.defalsify.org/vise.git v0.3.2-0.20250401123711-d481b04a6805/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ= git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ=
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310093912-8145b4bd004b h1:xiTpaqWWoF5qcnarY/9ZkT6aVdnKwqztb2gzIahJn4w= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69 h1:cbBpm9uNJak58MpFpNXJuvgCmz+A8kquXr9har4expg=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310093912-8145b4bd004b/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401111804-2eed990921c5 h1:DwBZHP4sebfHxK8EU2nlA2CXU81+a7Kj/pnC5vDPcf4= git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244 h1:BXotWSKg04U97sf/xeWJuUTSVgKk2aEK+5BtBrnafXQ=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401111804-2eed990921c5/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8= git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244/go.mod h1:6B6ByxXOiRY0NR7K02Bf3fEu7z+2c/6q8PFVNjC5G8w=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401115503-5b41c8dc6440 h1:eWrBZMM3pBMCFyRl4rO/aaR+OmOMFJxogNyFAFry+EM=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401115503-5b41c8dc6440/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401122510-441e289854ad h1:tYjanaCf6mF+iXRtDx5gckQm5vhZYx9N/JlNIBZj1m0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401122510-441e289854ad/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250411080608-34957e5b6ff8 h1:Emesd0rybSLhPwZwqdvLsk/P9ZsT+7CQwQV/mrjQp3o=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250411080608-34957e5b6ff8/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2 h1:YFztSsexCUgFo6M0tbngRwYdgJd3LQV3RO/Jw09u3+k=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2/go.mod h1:6B6ByxXOiRY0NR7K02Bf3fEu7z+2c/6q8PFVNjC5G8w=
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E=
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694/go.mod h1:DpibtYpnT3nG4Kn556hRAkdu4+CtiI/6MbnQHal51mQ= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694/go.mod h1:DpibtYpnT3nG4Kn556hRAkdu4+CtiI/6MbnQHal51mQ=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=

View File

@ -1,14 +1,11 @@
package application package application
import ( import (
"bytes"
"context" "context"
"errors"
"fmt" "fmt"
"path" "path"
"strconv" "strconv"
"strings" "strings"
"unicode"
"gopkg.in/leonelquinteros/gotext.v1" "gopkg.in/leonelquinteros/gotext.v1"
@ -196,7 +193,6 @@ func (h *MenuHandlers) createAccountNoExist(ctx context.Context, sessionId strin
data := map[storedb.DataTyp]string{ data := map[storedb.DataTyp]string{
storedb.DATA_TRACKING_ID: trackingId, storedb.DATA_TRACKING_ID: trackingId,
storedb.DATA_PUBLIC_KEY: publicKey, storedb.DATA_PUBLIC_KEY: publicKey,
storedb.DATA_ACCOUNT_ALIAS: "",
} }
store := h.userdataStore store := h.userdataStore
for key, value := range data { for key, value := range data {
@ -243,60 +239,26 @@ func (h *MenuHandlers) CreateAccount(ctx context.Context, sym string, input []by
return res, nil return res, nil
} }
func (h *MenuHandlers) CheckAccountCreated(ctx context.Context, sym string, input []byte) (resource.Result, error) { // ResetValidPin resets the flag_valid_pin flag.
func (h *MenuHandlers) ResetValidPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
flag_language_set, _ := h.flagManager.GetFlag("flag_language_set") flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin")
flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") res.FlagReset = append(res.FlagReset, flag_valid_pin)
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
_, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
if db.IsNotFound(err) {
// reset major flags
res.FlagReset = append(res.FlagReset, flag_language_set)
res.FlagReset = append(res.FlagReset, flag_account_created)
return res, nil return res, nil
} }
return res, nil // CheckBlockedStatus resets the account blocked flag if the PIN attempts have been reset by an admin.
}
res.FlagSet = append(res.FlagSet, flag_account_created)
return res, nil
}
// CheckBlockedStatus:
// 1. Checks whether the DATA_SELF_PIN_RESET is 1 and sets the flag_account_pin_reset
// 2. resets the account blocked flag if the PIN attempts have been reset by an admin.
func (h *MenuHandlers) CheckBlockedStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) CheckBlockedStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
store := h.userdataStore store := h.userdataStore
flag_account_blocked, _ := h.flagManager.GetFlag("flag_account_blocked") flag_account_blocked, _ := h.flagManager.GetFlag("flag_account_blocked")
flag_account_pin_reset, _ := h.flagManager.GetFlag("flag_account_pin_reset")
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
res.FlagReset = append(res.FlagReset, flag_account_pin_reset)
selfPinReset, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SELF_PIN_RESET)
if err == nil {
pinResetValue, _ := strconv.ParseUint(string(selfPinReset), 0, 64)
if pinResetValue == 1 {
res.FlagSet = append(res.FlagSet, flag_account_pin_reset)
}
}
currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INCORRECT_PIN_ATTEMPTS) currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INCORRECT_PIN_ATTEMPTS)
if err != nil { if err != nil {
if !db.IsNotFound(err) { if !db.IsNotFound(err) {
@ -305,6 +267,7 @@ func (h *MenuHandlers) CheckBlockedStatus(ctx context.Context, sym string, input
} }
pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64) pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64)
if pinAttemptsValue == 0 { if pinAttemptsValue == 0 {
res.FlagReset = append(res.FlagReset, flag_account_blocked) res.FlagReset = append(res.FlagReset, flag_account_blocked)
return res, nil return res, nil
@ -347,6 +310,29 @@ func (h *MenuHandlers) ResetIncorrectPin(ctx context.Context, sym string, input
return res, nil return res, nil
} }
// VerifyNewPin checks if a new PIN meets the required format criteria.
func (h *MenuHandlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
res := resource.Result{}
_, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin")
if !h.st.Back() {
pinInput := string(input)
// Validate that the PIN is a 4-digit number.
if pin.IsValidPIN(pinInput) {
res.FlagSet = append(res.FlagSet, flag_valid_pin)
} else {
res.FlagReset = append(res.FlagReset, flag_valid_pin)
}
} else {
res.FlagSet = append(res.FlagSet, flag_valid_pin)
}
return res, nil
}
// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE, // SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE,
// during the account creation process // during the account creation process
// and during the change PIN process. // and during the change PIN process.
@ -359,20 +345,15 @@ func (h *MenuHandlers) SaveTemporaryPin(ctx context.Context, sym string, input [
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
if string(input) == "0" {
return res, nil
}
accountPIN := string(input) accountPIN := string(input)
// Validate that the PIN has a valid format. // Validate that the PIN is a 4-digit number.
if !pin.IsValidPIN(accountPIN) { if !pin.IsValidPIN(accountPIN) {
res.FlagSet = append(res.FlagSet, flag_invalid_pin) res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
return res, nil return res, nil
} }
res.FlagReset = append(res.FlagReset, flag_invalid_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
// Hash the PIN // Hash the PIN
hashedPIN, err := pin.HashPIN(string(accountPIN)) hashedPIN, err := pin.HashPIN(string(accountPIN))
@ -391,11 +372,78 @@ func (h *MenuHandlers) SaveTemporaryPin(ctx context.Context, sym string, input [
return res, nil return res, nil
} }
// ResetInvalidPIN resets the invalid PIN flag // SaveOthersTemporaryPin allows authorized users to set temporary PINs for blocked numbers.
func (h *MenuHandlers) ResetInvalidPIN(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin") var err error
res.FlagReset = append(res.FlagReset, flag_invalid_pin)
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
temporaryPin := string(input)
// Retrieve the blocked number associated with this session
blockedNumber, err := store.ReadEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err)
return res, err
}
// Hash the temporary PIN
hashedPIN, err := pin.HashPIN(string(temporaryPin))
if err != nil {
logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err)
return res, err
}
// Save the hashed temporary PIN for that blocked number
err = store.WriteEntry(ctx, string(blockedNumber), storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write hashed temporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err)
return res, err
}
return res, nil
}
// CheckBlockedNumPinMisMatch checks if the provided PIN matches a temporary PIN stored for a blocked number.
func (h *MenuHandlers) CheckBlockedNumPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) {
res := resource.Result{}
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
if h.st.Back() {
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
return res, nil
}
// Get blocked number from storage.
store := h.userdataStore
blockedNumber, err := store.ReadEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err)
return res, err
}
// Get Hashed temporary PIN for the blocked number.
hashedTemporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), storedb.DATA_TEMPORARY_VALUE)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
return res, err
}
if len(hashedTemporaryPin) == 0 {
logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE)
return res, fmt.Errorf("Data error encountered")
}
if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) {
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
} else {
res.FlagSet = append(res.FlagSet, flag_pin_mismatch)
}
return res, nil return res, nil
} }
@ -407,9 +455,8 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
flag_account_pin_reset, _ := h.flagManager.GetFlag("flag_account_pin_reset")
if string(input) == "0" { if h.st.Back() {
res.FlagReset = append(res.FlagReset, flag_pin_mismatch) res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
return res, nil return res, nil
} }
@ -438,68 +485,14 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [
logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", storedb.DATA_ACCOUNT_PIN, "hashedPIN value", hashedTemporaryPin, "error", err) logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", storedb.DATA_ACCOUNT_PIN, "hashedPIN value", hashedTemporaryPin, "error", err)
return res, err return res, err
} }
// set the DATA_SELF_PIN_RESET as 0
err = store.WriteEntry(ctx, sessionId, storedb.DATA_SELF_PIN_RESET, []byte("0"))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write DATA_SELF_PIN_RESET entry with", "key", storedb.DATA_SELF_PIN_RESET, "self PIN reset value", "0", "error", err)
return res, err
}
res.FlagReset = append(res.FlagReset, flag_account_pin_reset)
return res, nil
}
// ValidateBlockedNumber performs validation of phone numbers during the Reset other's PIN.
// It checks phone number format and verifies registration status.
// If valid, it writes the number under DATA_BLOCKED_NUMBER on the admin account
func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number")
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
if string(input) == "0" {
res.FlagReset = append(res.FlagReset, flag_unregistered_number)
return res, nil
}
blockedNumber := string(input)
formattedNumber, err := phone.FormatPhoneNumber(blockedNumber)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", blockedNumber, "error", err)
return res, nil
}
_, err = store.ReadEntry(ctx, formattedNumber, storedb.DATA_PUBLIC_KEY)
if err != nil {
if db.IsNotFound(err) {
logg.InfoCtxf(ctx, "Invalid or unregistered number")
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
return res, nil
} else {
logg.ErrorCtxf(ctx, "Error on ValidateBlockedNumber", "error", err)
return res, err
}
}
err = store.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(formattedNumber))
if err != nil {
return res, nil
}
return res, nil return res, nil
} }
// ResetOthersPin handles the PIN reset process for other users' accounts by: // ResetOthersPin handles the PIN reset process for other users' accounts by:
// 1. Retrieving the blocked phone number from the session // 1. Retrieving the blocked phone number from the session
// 2. Writing the DATA_SELF_PIN_RESET on the blocked phone number // 2. Fetching the hashed temporary PIN associated with that number
// 3. Resetting the DATA_INCORRECT_PIN_ATTEMPTS to 0 for the blocked phone number // 3. Updating the account PIN with the temporary PIN
func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
store := h.userdataStore store := h.userdataStore
@ -512,11 +505,19 @@ func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []b
logg.ErrorCtxf(ctx, "failed to read blockedPhonenumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err) logg.ErrorCtxf(ctx, "failed to read blockedPhonenumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err)
return res, err return res, err
} }
hashedTemporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), storedb.DATA_TEMPORARY_VALUE)
// set the DATA_SELF_PIN_RESET for the account
err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_SELF_PIN_RESET, []byte("1"))
if err != nil { if err != nil {
return res, nil logg.ErrorCtxf(ctx, "failed to read hashedTmporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
return res, err
}
if len(hashedTemporaryPin) == 0 {
logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE)
return res, fmt.Errorf("Data error encountered")
}
err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin))
if err != nil {
return res, err
} }
err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0"))) err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0")))
@ -587,26 +588,60 @@ func (h *MenuHandlers) ResetUnregisteredNumber(ctx context.Context, sym string,
return res, nil return res, nil
} }
// ValidateBlockedNumber performs validation of phone numbers, specifically for blocked numbers in the system.
// It checks phone number format and verifies registration status.
func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number")
store := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
if h.st.Back() {
res.FlagReset = append(res.FlagReset, flag_unregistered_number)
return res, nil
}
blockedNumber := string(input)
_, err = store.ReadEntry(ctx, blockedNumber, storedb.DATA_PUBLIC_KEY)
if !phone.IsValidPhoneNumber(blockedNumber) {
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
return res, nil
}
if err != nil {
if db.IsNotFound(err) {
logg.InfoCtxf(ctx, "Invalid or unregistered number")
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
return res, nil
} else {
logg.ErrorCtxf(ctx, "Error on ValidateBlockedNumber", "error", err)
return res, err
}
}
err = store.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(blockedNumber))
if err != nil {
return res, nil
}
return res, nil
}
// VerifyCreatePin checks whether the confirmation PIN is similar to the temporary PIN // VerifyCreatePin checks whether the confirmation PIN is similar to the temporary PIN
// If similar, it sets the USERFLAG_PIN_SET flag and writes the account PIN allowing the user // If similar, it sets the USERFLAG_PIN_SET flag and writes the account PIN allowing the user
// to access the main menu. // to access the main menu.
func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin")
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
flag_pin_set, _ := h.flagManager.GetFlag("flag_pin_set") flag_pin_set, _ := h.flagManager.GetFlag("flag_pin_set")
if string(input) == "0" { sessionId, ok := ctx.Value("SessionId").(string)
res.FlagReset = append(res.FlagReset, flag_pin_mismatch) if !ok {
return res, nil return res, fmt.Errorf("missing session")
} }
store := h.userdataStore store := h.userdataStore
hashedTemporaryPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) hashedTemporaryPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil { if err != nil {
@ -619,15 +654,14 @@ func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []
} }
if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) { if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) {
res.FlagSet = append(res.FlagSet, flag_valid_pin) res.FlagSet = []uint32{flag_valid_pin}
res.FlagReset = []uint32{flag_pin_mismatch}
res.FlagSet = append(res.FlagSet, flag_pin_set) res.FlagSet = append(res.FlagSet, flag_pin_set)
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
} else { } else {
res.FlagSet = append(res.FlagSet, flag_pin_mismatch) res.FlagSet = []uint32{flag_pin_mismatch}
return res, nil return res, nil
} }
// save the hashed PIN as the new account PIN
err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin)) err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin))
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err) logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err)
@ -663,6 +697,10 @@ func (h *MenuHandlers) SaveFirstname(ctx context.Context, sym string, input []by
logg.ErrorCtxf(ctx, "failed to write firstName entry with", "key", storedb.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) logg.ErrorCtxf(ctx, "failed to write firstName entry with", "key", storedb.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err)
return res, err return res, err
} }
err := h.constructAccountAlias(ctx)
if err != nil {
return res, err
}
res.FlagSet = append(res.FlagSet, flag_firstname_set) res.FlagSet = append(res.FlagSet, flag_firstname_set)
} else { } else {
if firstNameSet { if firstNameSet {
@ -1056,22 +1094,6 @@ func (h *MenuHandlers) GetCurrentProfileInfo(ctx context.Context, sym string, in
} }
res.FlagSet = append(res.FlagSet, flag_offerings_set) res.FlagSet = append(res.FlagSet, flag_offerings_set)
res.Content = string(profileInfo) res.Content = string(profileInfo)
case storedb.DATA_ACCOUNT_ALIAS:
profileInfo, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS)
if err != nil {
if db.IsNotFound(err) {
res.Content = defaultValue
break
}
logg.ErrorCtxf(ctx, "Failed to read account alias entry with", "key", "error", storedb.DATA_ACCOUNT_ALIAS, err)
return res, err
}
alias := string(profileInfo)
if alias == "" {
res.Content = defaultValue
} else {
res.Content = alias
}
default: default:
break break
} }
@ -1115,10 +1137,8 @@ func (h *MenuHandlers) GetProfileInfo(ctx context.Context, sym string, input []b
offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS)) offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS))
alias := getEntryOrDefault(store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS)) alias := getEntryOrDefault(store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS))
if alias != defaultValue && alias != "" { if alias != defaultValue {
alias = strings.Split(alias, ".")[0] alias = strings.Split(alias, ".")[0]
} else {
alias = defaultValue
} }
// Construct the full name // Construct the full name
@ -1203,6 +1223,10 @@ func (h *MenuHandlers) UpdateAllProfileItems(ctx context.Context, sym string, in
if err != nil { if err != nil {
return res, err return res, err
} }
err = h.constructAccountAlias(ctx)
if err != nil {
return res, err
}
return res, nil return res, nil
} }
@ -1241,7 +1265,6 @@ func (h *MenuHandlers) Authorize(ctx context.Context, sym string, input []byte)
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized")
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin")
store := h.userdataStore store := h.userdataStore
AccountPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN) AccountPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN)
@ -1278,9 +1301,6 @@ func (h *MenuHandlers) Authorize(ctx context.Context, sym string, input []byte)
return res, nil return res, nil
} }
} else { } else {
if string(input) != "0" {
res.FlagSet = append(res.FlagSet, flag_invalid_pin)
}
return res, nil return res, nil
} }
return res, nil return res, nil
@ -1381,83 +1401,53 @@ func (h *MenuHandlers) ShowBlockedAccount(ctx context.Context, sym string, input
return res, nil return res, nil
} }
// loadUserContent loads the main user content in the main menu: the alias,balance associated with active voucher
func loadUserContent(ctx context.Context, activeSym string, balance string, alias string) (string, error) {
var content string
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
balFloat, err := strconv.ParseFloat(balance, 64)
if err != nil {
//Only exclude ErrSyntax error to avoid returning an error if the active bal is not available yet
if !errors.Is(err, strconv.ErrSyntax) {
logg.ErrorCtxf(ctx, "failed to parse activeBal as float", "value", balance, "error", err)
return "", err
}
balFloat = 0.00
}
// Format to 2 decimal places
balStr := fmt.Sprintf("%.2f %s", balFloat, activeSym)
if alias != "" {
content = l.Get("%s balance: %s\n", alias, balStr)
} else {
content = l.Get("Balance: %s\n", balStr)
}
return content, nil
}
// CheckBalance retrieves the balance of the active voucher and sets // CheckBalance retrieves the balance of the active voucher and sets
// the balance as the result content. // the balance as the result content.
func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var ( var res resource.Result
res resource.Result var err error
err error
alias string
content string
)
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
store := h.userdataStore store := h.userdataStore
// get the active sym and active balance // get the active sym and active balance
activeSym, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) activeSym, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if err != nil { if err != nil {
logg.InfoCtxf(ctx, "could not find the activeSym in checkBalance:", "err", err) if db.IsNotFound(err) {
if !db.IsNotFound(err) { balance := "0.00"
res.Content = l.Get("Balance: %s\n", balance)
return res, nil
}
logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err)
return res, err return res, err
} }
}
activeBal, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL) activeBal, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL)
if err != nil { if err != nil {
if !db.IsNotFound(err) {
logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err)
return res, err return res, err
} }
}
accAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) // Convert activeBal from []byte to float64
balFloat, err := strconv.ParseFloat(string(activeBal), 64)
if err != nil { if err != nil {
if !db.IsNotFound(err) { logg.ErrorCtxf(ctx, "failed to parse activeBal as float", "value", string(activeBal), "error", err)
logg.ErrorCtxf(ctx, "failed to read account alias entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "error", err)
return res, err return res, err
} }
} else {
alias = strings.Split(string(accAlias), ".")[0]
}
content, err = loadUserContent(ctx, string(activeSym), string(activeBal), alias) // Format to 2 decimal places
if err != nil { balStr := fmt.Sprintf("%.2f %s", balFloat, activeSym)
return res, err
} res.Content = l.Get("Balance: %s\n", balStr)
res.Content = content
return res, nil return res, nil
} }
@ -1561,7 +1551,6 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
//Perform a search for each search domain,break on first match //Perform a search for each search domain,break on first match
for _, domain := range config.SearchDomains() { for _, domain := range config.SearchDomains() {
fqdn := fmt.Sprintf("%s.%s", recipient, domain) fqdn := fmt.Sprintf("%s.%s", recipient, domain)
logg.InfoCtxf(ctx, "Resolving with fqdn alias", "alias", fqdn)
AliasAddress, err = h.accountService.CheckAliasAddress(ctx, fqdn) AliasAddress, err = h.accountService.CheckAliasAddress(ctx, fqdn)
if err == nil { if err == nil {
AliasAddressResult = AliasAddress.Address AliasAddressResult = AliasAddress.Address
@ -1631,25 +1620,20 @@ func (h *MenuHandlers) InviteValidRecipient(ctx context.Context, sym string, inp
l := gotext.NewLocale(translationDir, code) l := gotext.NewLocale(translationDir, code)
l.AddDomain("default") l.AddDomain("default")
recipient, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil { if len(recipient) == 0 {
logg.ErrorCtxf(ctx, "Failed to read invalid recipient info", "error", err) logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE)
return res, err return res, fmt.Errorf("Data error encountered")
} }
if !phone.IsValidPhoneNumber(string(recipient)) { // TODO
logg.InfoCtxf(ctx, "corrupted recipient", "key", storedb.DATA_TEMPORARY_VALUE, "recipient", recipient) // send an invitation SMS
return res, nil // if successful
} // res.Content = l.Get("Your invitation to %s to join Sarafu Network has been sent.", string(recipient))
_, err = h.accountService.SendUpsellSMS(ctx, sessionId, string(recipient))
if err != nil {
res.Content = l.Get("Your invite request for %s to Sarafu Network failed. Please try again later.", string(recipient)) res.Content = l.Get("Your invite request for %s to Sarafu Network failed. Please try again later.", string(recipient))
return res, nil return res, nil
} }
res.Content = l.Get("Your invitation to %s to join Sarafu Network has been sent.", string(recipient))
return res, nil
}
// ResetTransactionAmount resets the transaction amount and invalid flag. // ResetTransactionAmount resets the transaction amount and invalid flag.
func (h *MenuHandlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) {
@ -1873,12 +1857,11 @@ func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, inpu
return res, nil return res, nil
} }
// ManageVouchers retrieves the token holdings from the API using the "PublicKey" and // SetDefaultVoucher retrieves the current vouchers
// 1. sets the first as the default voucher if no active voucher is set. // and sets the first as the default voucher, if no active voucher is set.
// 2. Stores list of vouchers func (h *MenuHandlers) SetDefaultVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
// 3. updates the balance of the active voucher
func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
var err error
userStore := h.userdataStore userStore := h.userdataStore
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
@ -1888,31 +1871,31 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher")
// check if the user has an active sym
_, err = userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if err != nil {
if db.IsNotFound(err) {
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err) logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err)
return res, err return res, err
} }
// Fetch vouchers from API // Fetch vouchers from the API using the public key
vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_no_active_voucher) res.FlagSet = append(res.FlagSet, flag_no_active_voucher)
return res, nil return res, nil
} }
// Return if there is no voucher
if len(vouchersResp) == 0 { if len(vouchersResp) == 0 {
res.FlagSet = append(res.FlagSet, flag_no_active_voucher) res.FlagSet = append(res.FlagSet, flag_no_active_voucher)
return res, nil return res, nil
} }
res.FlagReset = append(res.FlagReset, flag_no_active_voucher) // Use only the first voucher
// Check if user has an active voucher with proper error handling
activeSym, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if err != nil {
if db.IsNotFound(err) {
// No active voucher, set the first one as default
firstVoucher := vouchersResp[0] firstVoucher := vouchersResp[0]
defaultSym := firstVoucher.TokenSymbol defaultSym := firstVoucher.TokenSymbol
defaultBal := firstVoucher.Balance defaultBal := firstVoucher.Balance
@ -1922,27 +1905,69 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
// Scale down the balance // Scale down the balance
scaledBalance := store.ScaleDownBalance(defaultBal, defaultDec) scaledBalance := store.ScaleDownBalance(defaultBal, defaultDec)
firstVoucherMap := map[storedb.DataTyp]string{ // TODO: implement atomic transaction
storedb.DATA_ACTIVE_SYM: defaultSym, // set the active symbol
storedb.DATA_ACTIVE_BAL: scaledBalance, err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte(defaultSym))
storedb.DATA_ACTIVE_DECIMAL: defaultDec, if err != nil {
storedb.DATA_ACTIVE_ADDRESS: defaultAddr, logg.ErrorCtxf(ctx, "failed to write defaultSym entry with", "key", storedb.DATA_ACTIVE_SYM, "value", defaultSym, "error", err)
}
for key, value := range firstVoucherMap {
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write active voucher data", "key", key, "error", err)
return res, err return res, err
} }
// set the active balance
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL, []byte(scaledBalance))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write defaultBal entry with", "key", storedb.DATA_ACTIVE_BAL, "value", scaledBalance, "error", err)
return res, err
}
// set the active decimals
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL, []byte(defaultDec))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write defaultDec entry with", "key", storedb.DATA_ACTIVE_DECIMAL, "value", defaultDec, "error", err)
return res, err
}
// set the active contract address
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS, []byte(defaultAddr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write defaultAddr entry with", "key", storedb.DATA_ACTIVE_ADDRESS, "value", defaultAddr, "error", err)
return res, err
}
return res, nil
} }
logg.InfoCtxf(ctx, "Default voucher set", "symbol", defaultSym, "balance", defaultBal, "decimals", defaultDec, "address", defaultAddr)
} else {
logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err)
return res, err return res, err
} }
} else {
// Update active voucher balance res.FlagReset = append(res.FlagReset, flag_no_active_voucher)
return res, nil
}
// CheckVouchers retrieves the token holdings from the API using the "PublicKey" and stores
// them to gdbm.
func (h *MenuHandlers) CheckVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
userStore := h.userdataStore
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err)
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
}
// check the current active sym and update the data
activeSym, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if activeSym != nil {
activeSymStr := string(activeSym) activeSymStr := string(activeSym)
// Find the matching voucher data // Find the matching voucher data
@ -1972,8 +1997,9 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
} }
} }
// Store all voucher data
data := store.ProcessVouchers(vouchersResp) data := store.ProcessVouchers(vouchersResp)
// Store all voucher data
dataMap := map[storedb.DataTyp]string{ dataMap := map[storedb.DataTyp]string{
storedb.DATA_VOUCHER_SYMBOLS: data.Symbols, storedb.DATA_VOUCHER_SYMBOLS: data.Symbols,
storedb.DATA_VOUCHER_BALANCES: data.Balances, storedb.DATA_VOUCHER_BALANCES: data.Balances,
@ -1981,11 +2007,9 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
storedb.DATA_VOUCHER_ADDRESSES: data.Addresses, storedb.DATA_VOUCHER_ADDRESSES: data.Addresses,
} }
// Write data entries
for key, value := range dataMap { for key, value := range dataMap {
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { if err := h.prefixDb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write data entry for sessionId: %s", sessionId, "key", key, "error", err) return res, nil
continue
} }
} }
@ -1995,25 +2019,16 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
// GetVoucherList fetches the list of vouchers and formats them. // GetVoucherList fetches the list of vouchers and formats them.
func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
userStore := h.userdataStore
// Read vouchers from the store // Read vouchers from the store
voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS) voucherData, err := h.prefixDb.Get(ctx, storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS))
logg.InfoCtxf(ctx, "reading GetVoucherList entries for sessionId: %s", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "voucherData", voucherData)
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to read voucherData entires with", "key", storedb.DATA_VOUCHER_SYMBOLS, "error", err) logg.ErrorCtxf(ctx, "Failed to read the voucherData from prefixDb", "error", err)
return res, err return res, err
} }
formattedData := h.ReplaceSeparatorFunc(string(voucherData)) formattedData := h.ReplaceSeparatorFunc(string(voucherData))
logg.InfoCtxf(ctx, "final output for sessionId: %s", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "formattedData", formattedData)
res.Content = string(formattedData) res.Content = string(formattedData)
return res, nil return res, nil
@ -2040,7 +2055,7 @@ func (h *MenuHandlers) ViewVoucher(ctx context.Context, sym string, input []byte
return res, nil return res, nil
} }
metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr) metadata, err := store.GetVoucherData(ctx, h.prefixDb, inputStr)
if err != nil { if err != nil {
return res, fmt.Errorf("failed to retrieve voucher data: %v", err) return res, fmt.Errorf("failed to retrieve voucher data: %v", err)
} }
@ -2369,110 +2384,6 @@ func (h *MenuHandlers) constructAccountAlias(ctx context.Context) error {
return nil return nil
} }
// RequestCustomAlias requests an ENS based alias name based on a user's input,then saves it as temporary value
func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
if string(input) == "0" {
return res, nil
}
store := h.userdataStore
aliasHint, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil {
if db.IsNotFound(err) {
return res, nil
}
return res, err
}
//Ensures that the call doesn't happen twice for the same alias hint
if !bytes.Equal(aliasHint, input) {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(string(input)))
if err != nil {
return res, err
}
pubKey, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
if db.IsNotFound(err) {
return res, nil
}
}
sanitizedInput := sanitizeAliasHint(string(input))
aliasResult, err := h.accountService.RequestAlias(ctx, string(pubKey), sanitizedInput)
if err != nil {
logg.ErrorCtxf(ctx, "failed to retrieve alias", "alias", string(aliasHint), "error_alias_request", err)
return res, fmt.Errorf("Failed to retrieve alias: %s", err.Error())
}
alias := aliasResult.Alias
logg.InfoCtxf(ctx, "Suggested alias ", "alias", alias)
//Store the returned alias,wait for user to confirm it as new account alias
err = store.WriteEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS, []byte(alias))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write account alias", "key", storedb.DATA_TEMPORARY_VALUE, "value", alias, "error", err)
return res, err
}
}
return res, nil
}
func sanitizeAliasHint(input string) string {
for i, r := range input {
// Check if the character is a special character (non-alphanumeric)
if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
return input[:i]
}
}
// If no special character is found, return the whole input
return input
}
// GetSuggestedAlias loads and displays the suggested alias name from the temporary value
func (h *MenuHandlers) GetSuggestedAlias(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")
}
suggestedAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS)
if err != nil {
return res, nil
}
res.Content = string(suggestedAlias)
return res, nil
}
// ConfirmNewAlias reads the suggested alias from the [DATA_SUGGECTED_ALIAS] key and confirms it as the new account alias.
func (h *MenuHandlers) ConfirmNewAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
store := h.userdataStore
flag_alias_set, _ := h.flagManager.GetFlag("flag_alias_set")
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
newAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS)
if err != nil {
return res, nil
}
logg.InfoCtxf(ctx, "Confirming new alias", "alias", string(newAlias))
err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(string(newAlias)))
if err != nil {
logg.ErrorCtxf(ctx, "failed to clear DATA_ACCOUNT_ALIAS_VALUE entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "value", "empty", "error", err)
return res, err
}
res.FlagSet = append(res.FlagSet, flag_alias_set)
return res, nil
}
// ClearTemporaryValue empties the DATA_TEMPORARY_VALUE at the main menu to prevent // ClearTemporaryValue empties the DATA_TEMPORARY_VALUE at the main menu to prevent
// previously stored data from being accessed // previously stored data from being accessed
func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, input []byte) (resource.Result, error) {

View File

@ -566,7 +566,7 @@ func TestSaveTemporaryPin(t *testing.T) {
log.Fatal(err) log.Fatal(err)
} }
flag_invalid_pin, _ := fm.GetFlag("flag_invalid_pin") flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin")
// Create the MenuHandlers instance with the mock flag manager // Create the MenuHandlers instance with the mock flag manager
h := &MenuHandlers{ h := &MenuHandlers{
@ -584,14 +584,14 @@ func TestSaveTemporaryPin(t *testing.T) {
name: "Valid Pin entry", name: "Valid Pin entry",
input: []byte("1234"), input: []byte("1234"),
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagReset: []uint32{flag_invalid_pin}, FlagReset: []uint32{flag_incorrect_pin},
}, },
}, },
{ {
name: "Invalid Pin entry", name: "Invalid Pin entry",
input: []byte("12343"), input: []byte("12343"),
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_pin}, FlagSet: []uint32{flag_incorrect_pin},
}, },
}, },
} }
@ -1037,7 +1037,6 @@ func TestAuthorize(t *testing.T) {
flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin")
flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") flag_account_authorized, _ := fm.GetFlag("flag_account_authorized")
flag_allow_update, _ := fm.GetFlag("flag_allow_update") flag_allow_update, _ := fm.GetFlag("flag_allow_update")
flag_invalid_pin, _ := fm.GetFlag("flag_invalid_pin")
// Set 1234 is the correct account pin // Set 1234 is the correct account pin
accountPIN := "1234" accountPIN := "1234"
@ -1073,9 +1072,7 @@ func TestAuthorize(t *testing.T) {
{ {
name: "Test with pin that is not a 4 digit", name: "Test with pin that is not a 4 digit",
input: []byte("1235aqds"), input: []byte("1235aqds"),
expectedResult: resource.Result{ expectedResult: resource.Result{},
FlagSet: []uint32{flag_invalid_pin},
},
}, },
} }
@ -1843,7 +1840,53 @@ func TestGetProfile(t *testing.T) {
} }
} }
func TestConfirmPinChange(t *testing.T) { func TestVerifyNewPin(t *testing.T) {
sessionId := "session123"
fm, _ := NewFlagManager(flagsPath)
mockState := state.NewState(16)
flag_valid_pin, _ := fm.GetFlag("flag_valid_pin")
mockAccountService := new(mocks.MockAccountService)
h := &MenuHandlers{
flagManager: fm,
accountService: mockAccountService,
st: mockState,
}
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
tests := []struct {
name string
input []byte
expectedResult resource.Result
}{
{
name: "Test with valid pin",
input: []byte("1234"),
expectedResult: resource.Result{
FlagSet: []uint32{flag_valid_pin},
},
},
{
name: "Test with invalid pin",
input: []byte("123"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_valid_pin},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
//Call the function under test
res, _ := h.VerifyNewPin(ctx, "verify_new_pin", tt.input)
//Assert that the result set to content is what was expected
assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input")
})
}
}
func TestConfirmPin(t *testing.T) {
sessionId := "session123" sessionId := "session123"
mockState := state.NewState(16) mockState := state.NewState(16)
@ -1852,8 +1895,6 @@ func TestConfirmPinChange(t *testing.T) {
fm, _ := NewFlagManager(flagsPath) fm, _ := NewFlagManager(flagsPath)
flag_pin_mismatch, _ := fm.GetFlag("flag_pin_mismatch") flag_pin_mismatch, _ := fm.GetFlag("flag_pin_mismatch")
flag_account_pin_reset, _ := fm.GetFlag("flag_account_pin_reset")
mockAccountService := new(mocks.MockAccountService) mockAccountService := new(mocks.MockAccountService)
h := &MenuHandlers{ h := &MenuHandlers{
userdataStore: store, userdataStore: store,
@ -1873,7 +1914,7 @@ func TestConfirmPinChange(t *testing.T) {
input: []byte("1234"), input: []byte("1234"),
temporarypin: "1234", temporarypin: "1234",
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagReset: []uint32{flag_pin_mismatch, flag_account_pin_reset}, FlagReset: []uint32{flag_pin_mismatch},
}, },
}, },
} }
@ -1946,46 +1987,36 @@ func TestFetchCommunityBalance(t *testing.T) {
} }
} }
func TestManageVouchers(t *testing.T) { func TestSetDefaultVoucher(t *testing.T) {
sessionId := "session123" sessionId := "session123"
publicKey := "0X13242618721"
ctx, store := InitializeTestStore(t) ctx, store := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "SessionId", sessionId)
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
if err != nil { if err != nil {
t.Fatal(err) t.Logf(err.Error())
} }
flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher") flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher")
if err != nil { if err != nil {
t.Fatal(err) t.Logf(err.Error())
} }
err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) publicKey := "0X13242618721"
if err != nil {
t.Fatal(err)
}
tests := []struct { tests := []struct {
name string name string
vouchersResp []dataserviceapi.TokenHoldings vouchersResp []dataserviceapi.TokenHoldings
storedActiveVoucher string
expectedVoucherSymbols []byte
expectedUpdatedAddress []byte
expectedResult resource.Result expectedResult resource.Result
}{ }{
{ {
name: "No vouchers available", name: "Test no vouchers available",
vouchersResp: []dataserviceapi.TokenHoldings{}, vouchersResp: []dataserviceapi.TokenHoldings{},
expectedVoucherSymbols: []byte(""),
expectedUpdatedAddress: []byte(""),
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagSet: []uint32{flag_no_active_voucher}, FlagSet: []uint32{flag_no_active_voucher},
}, },
}, },
{ {
name: "Set default voucher when no active voucher is set", name: "Test set default voucher when no active voucher is set",
vouchersResp: []dataserviceapi.TokenHoldings{ vouchersResp: []dataserviceapi.TokenHoldings{
{ {
ContractAddress: "0x123", ContractAddress: "0x123",
@ -1994,24 +2025,7 @@ func TestManageVouchers(t *testing.T) {
Balance: "100", Balance: "100",
}, },
}, },
expectedVoucherSymbols: []byte("1:TOKEN1"), expectedResult: resource.Result{},
expectedUpdatedAddress: []byte("0x123"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_no_active_voucher},
},
},
{
name: "Check and update active voucher balance",
vouchersResp: []dataserviceapi.TokenHoldings{
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
},
storedActiveVoucher: "SRF",
expectedVoucherSymbols: []byte("1:SRF\n2:MILO"),
expectedUpdatedAddress: []byte("0xd4c288865Ce"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_no_active_voucher},
},
}, },
} }
@ -2025,11 +2039,51 @@ func TestManageVouchers(t *testing.T) {
flagManager: fm, flagManager: fm,
} }
err := store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
if err != nil {
t.Fatal(err)
}
mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil)
// Store active voucher if needed res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("some-input"))
if tt.storedActiveVoucher != "" {
err := store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte(tt.storedActiveVoucher)) assert.NoError(t, err)
assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result")
mockAccountService.AssertExpectations(t)
})
}
}
func TestCheckVouchers(t *testing.T) {
mockAccountService := new(mocks.MockAccountService)
sessionId := "session123"
publicKey := "0X13242618721"
ctx, store := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
spdb := InitializeTestSubPrefixDb(t, ctx)
h := &MenuHandlers{
userdataStore: store,
accountService: mockAccountService,
prefixDb: spdb,
}
err := store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
if err != nil {
t.Fatal(err)
}
mockVouchersResponse := []dataserviceapi.TokenHoldings{
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
}
// store the default voucher data
err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte("SRF"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -2037,45 +2091,52 @@ func TestManageVouchers(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
expectedSym := []byte("1:SRF\n2:MILO")
expectedUpdatedAddress := []byte("0xd4c288865Ce")
mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil)
_, err = h.CheckVouchers(ctx, "check_vouchers", []byte(""))
assert.NoError(t, err)
// Read voucher sym data from the store
voucherData, err := spdb.Get(ctx, storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS))
if err != nil {
t.Fatal(err)
} }
res, err := h.ManageVouchers(ctx, "manage_vouchers", []byte("")) // Read active contract address from the store
assert.NoError(t, err)
assert.Equal(t, tt.expectedResult, res)
if tt.storedActiveVoucher != "" {
// Validate stored voucher symbols
voucherData, err := store.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS)
assert.NoError(t, err)
assert.Equal(t, tt.expectedVoucherSymbols, voucherData)
// Validate stored active contract address
updatedAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS) updatedAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
assert.NoError(t, err) if err != nil {
assert.Equal(t, tt.expectedUpdatedAddress, updatedAddress) t.Fatal(err)
}
// assert that the data is stored correctly
assert.Equal(t, expectedSym, voucherData)
// assert that the address is updated
assert.Equal(t, expectedUpdatedAddress, updatedAddress)
mockAccountService.AssertExpectations(t) mockAccountService.AssertExpectations(t)
} }
})
}
}
func TestGetVoucherList(t *testing.T) { func TestGetVoucherList(t *testing.T) {
sessionId := "session123" sessionId := "session123"
ctx, store := InitializeTestStore(t) ctx := context.WithValue(context.Background(), "SessionId", sessionId)
ctx = context.WithValue(ctx, "SessionId", sessionId)
spdb := InitializeTestSubPrefixDb(t, ctx)
// Initialize MenuHandlers // Initialize MenuHandlers
h := &MenuHandlers{ h := &MenuHandlers{
userdataStore: store, prefixDb: spdb,
ReplaceSeparatorFunc: mockReplaceSeparator, ReplaceSeparatorFunc: mockReplaceSeparator,
} }
mockSyms := []byte("1:SRF\n2:MILO") mockSyms := []byte("1:SRF\n2:MILO")
// Put voucher sym data from the store // Put voucher sym data from the store
err := store.WriteEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS, mockSyms) err := spdb.Put(ctx, storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS), mockSyms)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -2095,11 +2156,15 @@ func TestViewVoucher(t *testing.T) {
} }
ctx, store := InitializeTestStore(t) ctx, store := InitializeTestStore(t)
sessionId := "session123" sessionId := "session123"
ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "SessionId", sessionId)
spdb := InitializeTestSubPrefixDb(t, ctx)
h := &MenuHandlers{ h := &MenuHandlers{
userdataStore: store, userdataStore: store,
flagManager: fm, flagManager: fm,
prefixDb: spdb,
} }
// Define mock voucher data // Define mock voucher data
@ -2112,7 +2177,7 @@ func TestViewVoucher(t *testing.T) {
// Put the data // Put the data
for key, value := range mockData { for key, value := range mockData {
err := store.WriteEntry(ctx, sessionId, key, []byte(value)) err = spdb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -2292,8 +2357,10 @@ func TestCheckBlockedStatus(t *testing.T) {
if err != nil { if err != nil {
t.Logf(err.Error()) t.Logf(err.Error())
} }
flag_account_blocked, _ := fm.GetFlag("flag_account_blocked") flag_account_blocked, err := fm.GetFlag("flag_account_blocked")
flag_account_pin_reset, _ := fm.GetFlag("flag_account_pin_reset") if err != nil {
t.Logf(err.Error())
}
h := &MenuHandlers{ h := &MenuHandlers{
userdataStore: store, userdataStore: store,
@ -2308,15 +2375,13 @@ func TestCheckBlockedStatus(t *testing.T) {
{ {
name: "Currently blocked account", name: "Currently blocked account",
currentWrongPinAttempts: "4", currentWrongPinAttempts: "4",
expectedResult: resource.Result{ expectedResult: resource.Result{},
FlagReset: []uint32{flag_account_pin_reset},
},
}, },
{ {
name: "Account with 0 wrong PIN attempts", name: "Account with 0 wrong PIN attempts",
currentWrongPinAttempts: "0", currentWrongPinAttempts: "0",
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagReset: []uint32{flag_account_pin_reset, flag_account_blocked}, FlagReset: []uint32{flag_account_blocked},
}, },
}, },
} }
@ -2845,6 +2910,173 @@ func TestValidateBlockedNumber(t *testing.T) {
} }
} }
func TestSaveOthersTemporaryPin(t *testing.T) {
sessionId := "session123"
blockedNumber := "+254712345678"
testPin := "1234"
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
h := &MenuHandlers{
userdataStore: userStore,
}
tests := []struct {
name string
sessionId string
blockedNumber string
testPin string
setup func() error // Setup function for each test case
expectedError bool
verifyResult func(t *testing.T) // Function to verify the result
}{
{
name: "Missing Session ID",
sessionId: "", // Empty session ID
blockedNumber: blockedNumber,
testPin: testPin,
setup: nil,
expectedError: true,
verifyResult: nil,
},
{
name: "Failed to Read Blocked Number",
sessionId: sessionId,
blockedNumber: blockedNumber,
testPin: testPin,
setup: func() error {
// Do not write the blocked number to simulate a read failure
return nil
},
expectedError: true,
verifyResult: nil,
},
{
name: "Successfully save hashed PIN",
sessionId: sessionId,
blockedNumber: blockedNumber,
testPin: testPin,
setup: func() error {
// Write the blocked number to the store
return userStore.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(blockedNumber))
},
expectedError: false,
verifyResult: func(t *testing.T) {
// Read the stored hashed PIN
othersHashedPin, err := userStore.ReadEntry(ctx, blockedNumber, storedb.DATA_TEMPORARY_VALUE)
if err != nil {
t.Fatal(err)
}
// Verify that the stored hashed PIN matches the original PIN
if !pin.VerifyPIN(string(othersHashedPin), testPin) {
t.Fatal("stored hashed PIN does not match the original PIN")
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up the context with the session ID
ctx := context.WithValue(context.Background(), "SessionId", tt.sessionId)
// Run the setup function if provided
if tt.setup != nil {
err := tt.setup()
if err != nil {
t.Fatal(err)
}
}
// Call the function under test
_, err := h.SaveOthersTemporaryPin(ctx, "save_others_temporary_pin", []byte(tt.testPin))
// Assert the error
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
// Verify the result if a verification function is provided
if tt.verifyResult != nil {
tt.verifyResult(t)
}
})
}
}
func TestCheckBlockedNumPinMisMatch(t *testing.T) {
sessionId := "session123"
blockedNumber := "+254712345678"
testPin := "1234"
mockState := state.NewState(128)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
hashedPIN, err := pin.HashPIN(testPin)
if err != nil {
logg.ErrorCtxf(ctx, "failed to hash testPin", "error", err)
t.Fatal(err)
}
fm, err := NewFlagManager(flagsPath)
if err != nil {
t.Fatal(err)
}
flag_pin_mismatch, _ := fm.GetFlag("flag_pin_mismatch")
h := &MenuHandlers{
userdataStore: userStore,
st: mockState,
flagManager: fm,
}
// Write initial data to the store
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(blockedNumber))
if err != nil {
t.Fatal(err)
}
err = userStore.WriteEntry(ctx, blockedNumber, storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN))
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
input []byte
expectedResult resource.Result
}{
{
name: "Successful PIN match",
input: []byte(testPin),
expectedResult: resource.Result{
FlagReset: []uint32{flag_pin_mismatch},
},
},
{
name: "PIN mismatch",
input: []byte("1345"),
expectedResult: resource.Result{
FlagSet: []uint32{flag_pin_mismatch},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := h.CheckBlockedNumPinMisMatch(ctx, "sym", tt.input)
assert.NoError(t, err)
assert.Equal(t, tt.expectedResult, res)
})
}
}
func TestGetCurrentProfileInfo(t *testing.T) { func TestGetCurrentProfileInfo(t *testing.T) {
sessionId := "session123" sessionId := "session123"
ctx, store := InitializeTestStore(t) ctx, store := InitializeTestStore(t)
@ -3001,6 +3233,30 @@ func TestResetOthersPin(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestResetValidPin(t *testing.T) {
ctx := context.Background()
fm, err := NewFlagManager(flagsPath)
if err != nil {
t.Fatal(err)
}
flag_valid_pin, _ := fm.GetFlag("flag_valid_pin")
expectedResult := resource.Result{
FlagReset: []uint32{flag_valid_pin},
}
h := &MenuHandlers{
flagManager: fm,
}
res, err := h.ResetValidPin(ctx, "reset_valid_pin", []byte(""))
assert.NoError(t, err)
assert.Equal(t, expectedResult, res)
}
func TestResetUnregisteredNumber(t *testing.T) { func TestResetUnregisteredNumber(t *testing.T) {
ctx := context.Background() ctx := context.Background()
@ -3217,6 +3473,15 @@ func TestUpdateAllProfileItems(t *testing.T) {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
require.NoError(t, err) require.NoError(t, err)
aliasInput := fmt.Sprintf("%s%s", profileItems[0], profileItems[1])
// Mock the account alias response
mockAccountService.On(
"RequestAlias",
publicKey,
aliasInput,
).Return(&models.RequestAliasResult{Alias: "JohnDoe"}, nil)
// Call the function under test // Call the function under test
res, err := h.UpdateAllProfileItems(ctx, "symbol", nil) res, err := h.UpdateAllProfileItems(ctx, "symbol", nil)
assert.NoError(t, err) assert.NoError(t, err)
@ -3228,6 +3493,10 @@ func TestUpdateAllProfileItems(t *testing.T) {
assert.Equal(t, profileItems[i], string(storedValue)) assert.Equal(t, profileItems[i], string(storedValue))
} }
// Validate alias storage
storedAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS)
assert.NoError(t, err)
assert.Equal(t, "JohnDoe", string(storedAlias))
assert.Equal(t, expectedResult, res) assert.Equal(t, expectedResult, res)
} }

View File

@ -49,8 +49,9 @@ func (eu *EventsUpdater) updateToken(ctx context.Context, identity identity.Iden
// set default token to given symbol. // set default token to given symbol.
func (eu *EventsUpdater) updateDefaultToken(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore, activeSym string) error { func (eu *EventsUpdater) updateDefaultToken(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore, activeSym string) error {
pfxDb := toPrefixDb(userStore, identity.SessionId)
// TODO: the activeSym input should instead be newline separated list? // TODO: the activeSym input should instead be newline separated list?
tokenData, err := store.GetVoucherData(ctx, userStore, identity.SessionId, activeSym) tokenData, err := store.GetVoucherData(ctx, pfxDb, activeSym)
if err != nil { if err != nil {
return err return err
} }

View File

@ -99,18 +99,23 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob) ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob)
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob) ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob)
ls.DbRs.AddLocalFunc("initiate_transaction", appHandlers.InitiateTransaction) 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("confirm_pin_change", appHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance) ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance)
ls.DbRs.AddLocalFunc("manage_vouchers", appHandlers.ManageVouchers) 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("get_vouchers", appHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher)
ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher) ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher)
ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails) 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("validate_blocked_number", appHandlers.ValidateBlockedNumber)
ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber)
ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber)
ls.DbRs.AddLocalFunc("reset_others_pin", appHandlers.ResetOthersPin) 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("get_current_profile_info", appHandlers.GetCurrentProfileInfo)
ls.DbRs.AddLocalFunc("check_transactions", appHandlers.CheckTransactions) ls.DbRs.AddLocalFunc("check_transactions", appHandlers.CheckTransactions)
ls.DbRs.AddLocalFunc("get_transactions", appHandlers.GetTransactionsList) ls.DbRs.AddLocalFunc("get_transactions", appHandlers.GetTransactionsList)
@ -119,12 +124,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack) ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack)
ls.DbRs.AddLocalFunc("show_blocked_account", appHandlers.ShowBlockedAccount) ls.DbRs.AddLocalFunc("show_blocked_account", appHandlers.ShowBlockedAccount)
ls.DbRs.AddLocalFunc("clear_temporary_value", appHandlers.ClearTemporaryValue) ls.DbRs.AddLocalFunc("clear_temporary_value", appHandlers.ClearTemporaryValue)
ls.DbRs.AddLocalFunc("reset_invalid_pin", appHandlers.ResetInvalidPIN)
ls.DbRs.AddLocalFunc("request_custom_alias", appHandlers.RequestCustomAlias)
ls.DbRs.AddLocalFunc("get_suggested_alias", appHandlers.GetSuggestedAlias)
ls.DbRs.AddLocalFunc("confirm_new_alias", appHandlers.ConfirmNewAlias)
ls.DbRs.AddLocalFunc("check_account_created", appHandlers.CheckAccountCreated)
ls.first = appHandlers.Init ls.first = appHandlers.Init
return appHandlers, nil return appHandlers, nil

View File

@ -71,7 +71,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "5", "input": "5",
@ -108,11 +108,11 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "", "input": "",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "2", "input": "2",
@ -149,7 +149,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "5", "input": "5",
@ -173,7 +173,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "0", "input": "0",
@ -190,7 +190,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "5", "input": "5",
@ -202,7 +202,23 @@
}, },
{ {
"input": "0700000000", "input": "0700000000",
"expectedContent": "{secondary_session_id} will get a PIN reset request.\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" "expectedContent": "Please enter new PIN for: {secondary_session_id}\n0:Back"
},
{
"input": "11111",
"expectedContent": "The PIN you have entered is invalid.Please try a 4 digit number instead.\n1:Retry\n9:Quit"
},
{
"input": "1",
"expectedContent": "Please enter new PIN for: {secondary_session_id}\n0:Back"
},
{
"input": "1111",
"expectedContent": "Please confirm new PIN for: {secondary_session_id}\n0:Back"
},
{
"input": "1111",
"expectedContent": "Please enter your PIN:"
}, },
{ {
"input": "1234", "input": "1234",
@ -223,7 +239,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "5", "input": "5",
@ -248,7 +264,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "3", "input": "3",
@ -268,7 +284,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "{balance}\n\n0:Back\n9:Quit" "expectedContent": "Balance: {balance}\n\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@ -276,7 +292,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "0", "input": "0",
@ -293,7 +309,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "3", "input": "3",
@ -321,7 +337,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "0", "input": "0",
@ -338,7 +354,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -395,7 +411,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -432,7 +448,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -473,7 +489,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -510,7 +526,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -547,7 +563,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -584,7 +600,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",
@ -596,7 +612,7 @@
}, },
{ {
"input": "1234", "input": "1234",
"expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\nYour alias: Not Provided\n\n0:Back\n9:Quit" "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\nYour alias: \n\n0:Back\n9:Quit"
}, },
{ {
"input": "0", "input": "0",
@ -617,7 +633,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -21,7 +21,7 @@ var (
sessionID string sessionID string
src = rand.NewSource(42) src = rand.NewSource(42)
g = rand.New(src) g = rand.New(src)
secondarySessionId = "+254700000000" secondarySessionId = "0700000000"
) )
var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests") var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests")

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -9,7 +9,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "1", "input": "1",

View File

@ -116,7 +116,7 @@
}, },
{ {
"input": "3", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back"
}, },
{ {
"input": "6", "input": "6",

View File

@ -1,5 +1,5 @@
LOAD reset_transaction_amount 0 LOAD reset_transaction_amount 0
LOAD max_amount 40 LOAD max_amount 10
RELOAD max_amount RELOAD max_amount
MAP max_amount MAP max_amount
MOUT back 0 MOUT back 0

View File

@ -1,2 +0,0 @@
{{.retrieve_blocked_number}} will get a PIN reset request.
Please enter your PIN to confirm:

View File

@ -1,12 +0,0 @@
LOAD retrieve_blocked_number 0
RELOAD retrieve_blocked_number
MAP retrieve_blocked_number
MOUT back 0
MOUT quit 9
LOAD authorize_account 6
HALT
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
INCMP _ 0
INCMP quit 9
INCMP pin_reset_result *

View File

@ -1,2 +0,0 @@
{{.retrieve_blocked_number}} atapokea ombi la kuweka upya PIN.
Tafadhali weka PIN yako kudhibitisha:

View File

@ -1,7 +1,4 @@
MOUT back 0 LOAD save_temporary_pin 6
HALT HALT
INCMP _ 0
LOAD verify_create_pin 8 LOAD verify_create_pin 8
RELOAD verify_create_pin
CATCH pin_mismatch flag_pin_mismatch 1
INCMP account_creation * INCMP account_creation *

View File

@ -1,2 +0,0 @@
Your full alias will be: {{.get_suggested_alias}}
Please enter your PIN to confirm:

View File

@ -1,12 +0,0 @@
LOAD reset_invalid_pin 6
RELOAD reset_invalid_pin
LOAD get_suggested_alias 0
RELOAD get_suggested_alias
MAP get_suggested_alias
MOUT back 0
HALT
INCMP _ 0
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
CATCH invalid_pin flag_invalid_pin 1
CATCH update_alias flag_allow_update 1

View File

@ -1,2 +0,0 @@
Lakabu yako kamili itakuwa: {{.get_suggested_alias}}
Tafadhali weka PIN yako ili kuthibitisha:

View File

@ -0,0 +1 @@
Please confirm new PIN for: {{.retrieve_blocked_number}}

View File

@ -0,0 +1,14 @@
CATCH pin_entry flag_incorrect_pin 1
RELOAD retrieve_blocked_number
MAP retrieve_blocked_number
CATCH invalid_others_pin flag_valid_pin 0
CATCH pin_reset_result flag_account_authorized 1
LOAD save_others_temporary_pin 6
RELOAD save_others_temporary_pin
MOUT back 0
HALT
INCMP _ 0
LOAD check_pin_mismatch 6
RELOAD check_pin_mismatch
CATCH others_pin_mismatch flag_pin_mismatch 1
INCMP pin_entry *

View File

@ -0,0 +1 @@
Tafadhali thibitisha PIN mpya ya: {{.retrieve_blocked_number}}

View File

@ -1,7 +1,7 @@
LOAD confirm_pin_change 0
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
LOAD confirm_pin_change 0
RELOAD confirm_pin_change RELOAD confirm_pin_change
CATCH pin_mismatch flag_pin_mismatch 1 CATCH pin_reset_mismatch flag_pin_mismatch 1
INCMP pin_reset_success * INCMP * pin_reset_success

View File

@ -2,8 +2,8 @@ LOAD create_account 0
CATCH account_creation_failed flag_account_creation_failed 1 CATCH account_creation_failed flag_account_creation_failed 1
MOUT exit 0 MOUT exit 0
HALT HALT
INCMP quit 0
LOAD save_temporary_pin 6 LOAD save_temporary_pin 6
RELOAD save_temporary_pin RELOAD save_temporary_pin
CATCH invalid_pin flag_invalid_pin 1 CATCH . flag_incorrect_pin 1
INCMP quit 0
INCMP confirm_create_pin * INCMP confirm_create_pin *

View File

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

View File

@ -7,4 +7,4 @@ INCMP _ 0
LOAD validate_blocked_number 6 LOAD validate_blocked_number 6
RELOAD validate_blocked_number RELOAD validate_blocked_number
CATCH unregistered_number flag_unregistered_number 1 CATCH unregistered_number flag_unregistered_number 1
INCMP authorize_reset_others_pin * INCMP enter_others_new_pin *

View File

@ -0,0 +1 @@
Please enter new PIN for: {{.retrieve_blocked_number}}

View File

@ -0,0 +1,9 @@
LOAD retrieve_blocked_number 0
RELOAD retrieve_blocked_number
MAP retrieve_blocked_number
MOUT back 0
HALT
LOAD verify_new_pin 6
RELOAD verify_new_pin
INCMP _ 0
INCMP * confirm_others_new_pin

View File

@ -0,0 +1 @@
Tafadhali weka PIN mpya ya: {{.retrieve_blocked_number}}

View File

@ -0,0 +1 @@
The PIN you have entered is invalid.Please try a 4 digit number instead.

View File

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

View File

@ -1 +1 @@
The PIN you entered is invalid. The PIN must be a 4 digit number. The PIN you entered is invalid.The PIN must be different from your current PIN.For help call +254757628885

View File

@ -1,8 +1,3 @@
LOAD reset_invalid_pin 6 MOUT back 0
RELOAD reset_invalid_pin
MOUT retry 1
MOUT quit 9
HALT HALT
INCMP _ 1 INCMP _ 0
INCMP quit 9
INCMP . *

View File

@ -1 +1 @@
PIN uliyoweka si sahihi. PIN lazima iwe nambari 4. PIN mpya na udhibitisho wa PIN mpya hazilingani.Tafadhali jaribu tena.Kwa usaidizi piga simu +254757628885.

View File

@ -30,7 +30,3 @@ msgstr "Salio la Kikundi: 0.00"
msgid "Symbol: %s\nBalance: %s" msgid "Symbol: %s\nBalance: %s"
msgstr "Sarafu: %s\nSalio: %s" msgstr "Sarafu: %s\nSalio: %s"
msgid "%s balance: %s\n"
msgstr "%s salio: %s\n"

View File

@ -1,7 +1,9 @@
LOAD clear_temporary_value 2 LOAD clear_temporary_value 2
RELOAD clear_temporary_value RELOAD clear_temporary_value
LOAD manage_vouchers 160 LOAD set_default_voucher 8
RELOAD manage_vouchers RELOAD set_default_voucher
LOAD check_vouchers 10
RELOAD check_vouchers
LOAD check_balance 128 LOAD check_balance 128
RELOAD check_balance RELOAD check_balance
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1

View File

@ -1,4 +1,3 @@
LOAD authorize_account 16
LOAD reset_allow_update 0 LOAD reset_allow_update 0
MOUT profile 1 MOUT profile 1
MOUT change_language 2 MOUT change_language 2
@ -6,15 +5,13 @@ MOUT check_balance 3
MOUT check_statement 4 MOUT check_statement 4
MOUT pin_options 5 MOUT pin_options 5
MOUT my_address 6 MOUT my_address 6
MOUT my_account_alias 7
MOUT back 0 MOUT back 0
HALT HALT
INCMP ^ 0 INCMP main 0
INCMP edit_profile 1 INCMP edit_profile 1
INCMP change_language 2 INCMP change_language 2
INCMP balances 3 INCMP balances 3
INCMP check_statement 4 INCMP check_statement 4
INCMP pin_management 5 INCMP pin_management 5
INCMP address 6 INCMP address 6
INCMP my_account_alias 7
INCMP . * INCMP . *

View File

@ -1,2 +0,0 @@
Current alias: {{.get_current_profile_info}}
Enter your preferred alias:

View File

@ -1,8 +0,0 @@
LOAD get_current_profile_info 0
MAP get_current_profile_info
MOUT back 0
HALT
INCMP _ 0
LOAD request_custom_alias 0
RELOAD request_custom_alias
INCMP confirm_new_alias *

View File

@ -1 +0,0 @@
My Alias

View File

@ -1 +0,0 @@
Lakabu yangu

View File

@ -1,2 +0,0 @@
Lakabu ya sasa: {{.get_current_profile_info}}
Weka lakabu unalopendelea:

View File

@ -2,5 +2,6 @@ MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
RELOAD save_temporary_pin RELOAD save_temporary_pin
CATCH invalid_pin flag_invalid_pin 1 RELOAD verify_new_pin
INCMP confirm_pin_change * CATCH invalid_pin flag_valid_pin 0
INCMP * confirm_pin_change

View File

@ -4,5 +4,4 @@ HALT
INCMP _ 0 INCMP _ 0
RELOAD authorize_account RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin flag_incorrect_pin 1
CATCH invalid_pin flag_invalid_pin 1
INCMP new_pin * INCMP new_pin *

View File

@ -0,0 +1 @@
The PIN you have entered is not a match

View File

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

View File

@ -0,0 +1 @@
PIN uliyoweka hailingani.Jaribu tena.

View File

@ -1,9 +1,9 @@
LOAD set_back 6 LOAD set_back 6
LOAD authorize_account 16 LOAD authorize_account 5
LOAD reset_allow_update 4 LOAD reset_allow_update 4
LOAD verify_new_pin 2
LOAD save_temporary_pin 1 LOAD save_temporary_pin 1
LOAD reset_incorrect 0 LOAD reset_incorrect 0
LOAD reset_invalid_pin 6
MOUT change_pin 1 MOUT change_pin 1
MOUT reset_pin 2 MOUT reset_pin 2
MOUT back 0 MOUT back 0

View File

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

View File

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

View File

@ -0,0 +1 @@
PIN uliyoweka hailingani.Jaribu tena.

View File

@ -1,4 +1,3 @@
CATCH _ flag_account_authorized 0
LOAD retrieve_blocked_number 0 LOAD retrieve_blocked_number 0
MAP retrieve_blocked_number MAP retrieve_blocked_number
LOAD reset_others_pin 6 LOAD reset_others_pin 6

View File

@ -1,6 +1,5 @@
MOUT back 0 MOUT back 0
MOUT quit 9 MOUT quit 9
HALT HALT
INCMP ^ 0 INCMP main 0
INCMP quit 9 INCMP quit 9
INCMP . *

View File

@ -9,7 +9,7 @@ flag,flag_account_authorized,15,this is set to allow a user access guarded nodes
flag,flag_invalid_recipient,16,this is set when the transaction recipient is invalid flag,flag_invalid_recipient,16,this is set when the transaction recipient is invalid
flag,flag_invalid_recipient_with_invite,17,this is set when the transaction recipient is valid but not on the platform flag,flag_invalid_recipient_with_invite,17,this is set when the transaction recipient is valid but not on the platform
flag,flag_invalid_amount,18,this is set when the given transaction amount is invalid flag,flag_invalid_amount,18,this is set when the given transaction amount is invalid
flag,flag_incorrect_pin,19,this is set when the provided PIN does not match the current account's PIN flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN
flag,flag_valid_pin,20,this is set when the given PIN is valid flag,flag_valid_pin,20,this is set when the given PIN is valid
flag,flag_allow_update,21,this is set to allow a user to update their profile data flag,flag_allow_update,21,this is set to allow a user to update their profile data
flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth
@ -29,6 +29,4 @@ flag,flag_location_set,35,this is set when the location of the profile is set
flag,flag_offerings_set,36,this is set when the offerings of the profile is set flag,flag_offerings_set,36,this is set when the offerings of the profile is set
flag,flag_back_set,37,this is set when it is a back navigation flag,flag_back_set,37,this is set when it is a back navigation
flag,flag_account_blocked,38,this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded flag,flag_account_blocked,38,this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded
flag,flag_invalid_pin,39,this is set when the given PIN is invalid(is less than or more than 4 digits)
flag,flag_alias_set,40,this is set when an account alias has been assigned to a user
flag,flag_account_pin_reset,41,this is set on an account when an admin triggers a PIN reset for them

1 flag flag_language_set 8 checks whether the user has set their prefered language
9 flag flag_invalid_recipient 16 this is set when the transaction recipient is invalid
10 flag flag_invalid_recipient_with_invite 17 this is set when the transaction recipient is valid but not on the platform
11 flag flag_invalid_amount 18 this is set when the given transaction amount is invalid
12 flag flag_incorrect_pin 19 this is set when the provided PIN does not match the current account's PIN this is set when the provided PIN is invalid or does not match the current account's PIN
13 flag flag_valid_pin 20 this is set when the given PIN is valid
14 flag flag_allow_update 21 this is set to allow a user to update their profile data
15 flag flag_single_edit 22 this is set to allow a user to edit a single profile item such as year of birth
29 flag flag_offerings_set 36 this is set when the offerings of the profile is set
30 flag flag_back_set 37 this is set when it is a back navigation
31 flag flag_account_blocked 38 this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded
32
flag flag_alias_set 40 this is set when an account alias has been assigned to a user
flag flag_account_pin_reset 41 this is set on an account when an admin triggers a PIN reset for them

View File

@ -1,15 +1,12 @@
LOAD check_blocked_status 1 LOAD check_blocked_status 1
RELOAD check_blocked_status RELOAD check_blocked_status
LOAD check_account_created 2
RELOAD check_account_created
CATCH self_reset_pin flag_account_pin_reset 1
CATCH blocked_account flag_account_blocked 1 CATCH blocked_account flag_account_blocked 1
CATCH select_language flag_language_set 0 CATCH select_language flag_language_set 0
CATCH terms flag_account_created 0 CATCH terms flag_account_created 0
CATCH create_pin flag_pin_set 0
LOAD check_account_status 0 LOAD check_account_status 0
RELOAD check_account_status RELOAD check_account_status
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1
CATCH account_pending flag_account_pending 1 CATCH account_pending flag_account_pending 1
CATCH create_pin flag_pin_set 0
CATCH main flag_account_success 1 CATCH main flag_account_success 1
HALT HALT

View File

@ -1,2 +0,0 @@
A PIN reset has been done on your account.
Please enter a new four number PIN:

View File

@ -1,6 +0,0 @@
LOAD reset_invalid_pin 6
HALT
LOAD save_temporary_pin 1
RELOAD save_temporary_pin
CATCH invalid_pin flag_invalid_pin 1
INCMP confirm_pin_change *

View File

@ -1,2 +0,0 @@
Uwekaji upya wa PIN umefanyika kwenye akaunti yako.
Tafadhali weka PIN mpya ya nambari nne:

View File

@ -1 +0,0 @@
Your alias has been updated successfully

View File

@ -1,7 +0,0 @@
LOAD confirm_new_alias 0
RELOAD confirm_new_alias
MOUT back 0
MOUT quit 9
HALT
INCMP ^ 0
INCMP quit 9

View File

@ -1 +0,0 @@
Ombi lako la kubadilisha lakabu limefanikiwa.

View File

@ -14,6 +14,6 @@ import (
func New(ctx context.Context, storageService storage.StorageService) remote.AccountService { func New(ctx context.Context, storageService storage.StorageService) remote.AccountService {
return &httpremote.HTTPAccountService{ return &httpremote.HTTPAccountService{
SS: storageService, SS: storageService,
UseApi: true, UseApi: false,
} }
} }

View File

@ -63,10 +63,6 @@ const (
DATA_INITIAL_LANGUAGE_CODE DATA_INITIAL_LANGUAGE_CODE
//Fully qualified account alias string //Fully qualified account alias string
DATA_ACCOUNT_ALIAS DATA_ACCOUNT_ALIAS
//currently suggested alias by the api awaiting user's confirmation as accepted account alias
DATA_SUGGESTED_ALIAS
//Key used to store a value of 1 for a user to reset their own PIN once they access the menu.
DATA_SELF_PIN_RESET
) )
const ( const (
@ -134,8 +130,7 @@ func StringToDataTyp(str string) (DataTyp, error) {
return DATA_GENDER, nil return DATA_GENDER, nil
case "DATA_OFFERINGS": case "DATA_OFFERINGS":
return DATA_OFFERINGS, nil return DATA_OFFERINGS, nil
case "DATA_ACCOUNT_ALIAS":
return DATA_ACCOUNT_ALIAS, nil
default: default:
return 0, errors.New("invalid DataTyp string") return 0, errors.New("invalid DataTyp string")
} }

View File

@ -33,14 +33,11 @@ func (s *SubPrefixDb) toKey(k []byte) []byte {
func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) {
s.store.SetPrefix(db.DATATYPE_USERDATA) s.store.SetPrefix(db.DATATYPE_USERDATA)
key = s.toKey(key) key = s.toKey(key)
logg.InfoCtxf(ctx, "SubPrefixDb Get log", "key", string(key))
return s.store.Get(ctx, key) return s.store.Get(ctx, key)
} }
func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error { func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error {
s.store.SetPrefix(db.DATATYPE_USERDATA) s.store.SetPrefix(db.DATATYPE_USERDATA)
key = s.toKey(key) key = s.toKey(key)
logg.InfoCtxf(ctx, "SubPrefixDb Put log", "key", string(key))
return s.store.Put(ctx, key, val) return s.store.Put(ctx, key, val)
} }

View File

@ -68,7 +68,7 @@ func ScaleDownBalance(balance, decimals string) string {
} }
// GetVoucherData retrieves and matches voucher data // GetVoucherData retrieves and matches voucher data
func GetVoucherData(ctx context.Context, store DataStore, sessionId string, input string) (*dataserviceapi.TokenHoldings, error) { func GetVoucherData(ctx context.Context, db storedb.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) {
keys := []storedb.DataTyp{ keys := []storedb.DataTyp{
storedb.DATA_VOUCHER_SYMBOLS, storedb.DATA_VOUCHER_SYMBOLS,
storedb.DATA_VOUCHER_BALANCES, storedb.DATA_VOUCHER_BALANCES,
@ -78,9 +78,9 @@ func GetVoucherData(ctx context.Context, store DataStore, sessionId string, inpu
data := make(map[storedb.DataTyp]string) data := make(map[storedb.DataTyp]string)
for _, key := range keys { for _, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key) value, err := db.Get(ctx, storedb.ToBytes(key))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get data key %x: %v", key, err) return nil, fmt.Errorf("failed to get prefix key %x: %v", storedb.ToBytes(key), err)
} }
data[key] = string(value) data[key] = string(value)
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/alecthomas/assert/v2" "github.com/alecthomas/assert/v2"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
visedb "git.defalsify.org/vise.git/db"
memdb "git.defalsify.org/vise.git/db/mem" memdb "git.defalsify.org/vise.git/db/mem"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
@ -76,8 +77,16 @@ func TestProcessVouchers(t *testing.T) {
} }
func TestGetVoucherData(t *testing.T) { func TestGetVoucherData(t *testing.T) {
ctx, store := InitializeTestDb(t) ctx := context.Background()
sessionId := "session123"
db := memdb.NewMemDb()
err := db.Connect(ctx, "")
if err != nil {
t.Fatal(err)
}
prefix := storedb.ToBytes(visedb.DATATYPE_USERDATA)
spdb := storedb.NewSubPrefixDb(db, prefix)
// Test voucher data // Test voucher data
mockData := map[storedb.DataTyp][]byte{ mockData := map[storedb.DataTyp][]byte{
@ -89,13 +98,13 @@ func TestGetVoucherData(t *testing.T) {
// Put the data // Put the data
for key, value := range mockData { for key, value := range mockData {
err := store.WriteEntry(ctx, sessionId, key, []byte(value)) err = spdb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
result, err := GetVoucherData(ctx, store, sessionId, "1") result, err := GetVoucherData(ctx, spdb, "1")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "SRF", result.TokenSymbol) assert.Equal(t, "SRF", result.TokenSymbol)