Compare commits
2 Commits
03c32fd265
...
7241cdbfcb
Author | SHA1 | Date | |
---|---|---|---|
7241cdbfcb | |||
0480c02633 |
@ -1118,7 +1118,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data := processVouchers(vouchersResp.Result.Holdings)
|
data := utils.ProcessVouchers(vouchersResp.Result.Holdings)
|
||||||
|
|
||||||
// Store all voucher data
|
// Store all voucher data
|
||||||
dataMap := map[string]string{
|
dataMap := map[string]string{
|
||||||
@ -1137,31 +1137,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processVouchers converts holdings into formatted strings
|
|
||||||
func processVouchers(holdings []struct {
|
|
||||||
ContractAddress string `json:"contractAddress"`
|
|
||||||
TokenSymbol string `json:"tokenSymbol"`
|
|
||||||
TokenDecimals string `json:"tokenDecimals"`
|
|
||||||
Balance string `json:"balance"`
|
|
||||||
}) VoucherMetadata {
|
|
||||||
var data VoucherMetadata
|
|
||||||
var symbols, balances, decimals, addresses []string
|
|
||||||
|
|
||||||
for i, h := range holdings {
|
|
||||||
symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol))
|
|
||||||
balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance))
|
|
||||||
decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals))
|
|
||||||
addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress))
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Symbol = strings.Join(symbols, "\n")
|
|
||||||
data.Balance = strings.Join(balances, "\n")
|
|
||||||
data.Decimal = strings.Join(decimals, "\n")
|
|
||||||
data.Address = strings.Join(addresses, "\n")
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVoucherList fetches the list of vouchers and formats them
|
// GetVoucherList fetches the list of vouchers and formats them
|
||||||
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
@ -1193,8 +1168,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the voucher metadata using the PrefixDb interface
|
metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr)
|
||||||
metadata, err := 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)
|
||||||
}
|
}
|
||||||
@ -1204,7 +1178,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.storeTemporaryVoucher(ctx, sessionId, metadata); err != nil {
|
if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1214,86 +1188,9 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVoucherData retrieves and matches voucher data
|
// SetVoucher retrieves the temp voucher data and sets it as the active data
|
||||||
func getVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) {
|
|
||||||
keys := []string{"sym", "bal", "deci", "addr"}
|
|
||||||
data := make(map[string]string)
|
|
||||||
|
|
||||||
for _, key := range keys {
|
|
||||||
value, err := db.Get(ctx, []byte(key))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get %s: %v", key, err)
|
|
||||||
}
|
|
||||||
data[key] = string(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol, balance, decimal, address := matchVoucher(input,
|
|
||||||
data["sym"],
|
|
||||||
data["bal"],
|
|
||||||
data["deci"],
|
|
||||||
data["addr"])
|
|
||||||
|
|
||||||
if symbol == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &VoucherMetadata{
|
|
||||||
Symbol: symbol,
|
|
||||||
Balance: balance,
|
|
||||||
Decimal: decimal,
|
|
||||||
Address: address,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input.
|
|
||||||
func matchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) {
|
|
||||||
symList := strings.Split(symbols, "\n")
|
|
||||||
balList := strings.Split(balances, "\n")
|
|
||||||
decList := strings.Split(decimals, "\n")
|
|
||||||
addrList := strings.Split(addresses, "\n")
|
|
||||||
|
|
||||||
for i, sym := range symList {
|
|
||||||
parts := strings.SplitN(sym, ":", 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if input == parts[0] || strings.EqualFold(input, parts[1]) {
|
|
||||||
symbol = parts[1]
|
|
||||||
if i < len(balList) {
|
|
||||||
balance = strings.SplitN(balList[i], ":", 2)[1]
|
|
||||||
}
|
|
||||||
if i < len(decList) {
|
|
||||||
decimal = strings.SplitN(decList[i], ":", 2)[1]
|
|
||||||
}
|
|
||||||
if i < len(addrList) {
|
|
||||||
address = strings.SplitN(addrList[i], ":", 2)[1]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handlers) storeTemporaryVoucher(ctx context.Context, sessionId string, data *VoucherMetadata) error {
|
|
||||||
entries := map[utils.DataTyp][]byte{
|
|
||||||
utils.DATA_TEMPORARY_SYM: []byte(data.Symbol),
|
|
||||||
utils.DATA_TEMPORARY_BAL: []byte(data.Balance),
|
|
||||||
utils.DATA_TEMPORARY_DECIMAL: []byte(data.Decimal),
|
|
||||||
utils.DATA_TEMPORARY_ADDRESS: []byte(data.Address),
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range entries {
|
|
||||||
if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
var err error
|
|
||||||
|
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -1301,78 +1198,16 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get temporary data
|
// Get temporary data
|
||||||
tempData, err := h.getTemporaryVoucherData(ctx, sessionId)
|
tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set as active and clear temporary
|
// Set as active and clear temporary data
|
||||||
if err := h.updateVoucherData(ctx, sessionId, tempData); err != nil {
|
if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Content = tempData.Symbol
|
res.Content = tempData.Symbol
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) getTemporaryVoucherData(ctx context.Context, sessionId string) (*VoucherMetadata, error) {
|
|
||||||
store := h.userdataStore
|
|
||||||
|
|
||||||
keys := []utils.DataTyp{
|
|
||||||
utils.DATA_TEMPORARY_SYM,
|
|
||||||
utils.DATA_TEMPORARY_BAL,
|
|
||||||
utils.DATA_TEMPORARY_DECIMAL,
|
|
||||||
utils.DATA_TEMPORARY_ADDRESS,
|
|
||||||
}
|
|
||||||
|
|
||||||
data := &VoucherMetadata{}
|
|
||||||
values := make([][]byte, len(keys))
|
|
||||||
|
|
||||||
for i, key := range keys {
|
|
||||||
value, err := store.ReadEntry(ctx, sessionId, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
values[i] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Symbol = string(values[0])
|
|
||||||
data.Balance = string(values[1])
|
|
||||||
data.Decimal = string(values[2])
|
|
||||||
data.Address = string(values[3])
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handlers) updateVoucherData(ctx context.Context, sessionId string, data *VoucherMetadata) error {
|
|
||||||
// Set active voucher data
|
|
||||||
activeEntries := map[utils.DataTyp][]byte{
|
|
||||||
utils.DATA_ACTIVE_SYM: []byte(data.Symbol),
|
|
||||||
utils.DATA_ACTIVE_BAL: []byte(data.Balance),
|
|
||||||
utils.DATA_ACTIVE_DECIMAL: []byte(data.Decimal),
|
|
||||||
utils.DATA_ACTIVE_ADDRESS: []byte(data.Address),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear temporary voucher data
|
|
||||||
tempEntries := map[utils.DataTyp][]byte{
|
|
||||||
utils.DATA_TEMPORARY_SYM: []byte(""),
|
|
||||||
utils.DATA_TEMPORARY_BAL: []byte(""),
|
|
||||||
utils.DATA_TEMPORARY_DECIMAL: []byte(""),
|
|
||||||
utils.DATA_TEMPORARY_ADDRESS: []byte(""),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write all entries
|
|
||||||
for key, value := range activeEntries {
|
|
||||||
if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range tempEntries {
|
|
||||||
if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -2050,29 +2050,6 @@ func TestCheckVouchers(t *testing.T) {
|
|||||||
mockAccountService.AssertExpectations(t)
|
mockAccountService.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProcessVouchers(t *testing.T) {
|
|
||||||
holdings := []struct {
|
|
||||||
ContractAddress string `json:"contractAddress"`
|
|
||||||
TokenSymbol string `json:"tokenSymbol"`
|
|
||||||
TokenDecimals string `json:"tokenDecimals"`
|
|
||||||
Balance string `json:"balance"`
|
|
||||||
}{
|
|
||||||
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
|
|
||||||
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedResult := VoucherMetadata{
|
|
||||||
Symbol: "1:SRF\n2:MILO",
|
|
||||||
Balance: "1:100\n2:200",
|
|
||||||
Decimal: "1:6\n2:4",
|
|
||||||
Address: "1:0xd4c288865Ce\n2:0x41c188d63Qa",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := processVouchers(holdings)
|
|
||||||
|
|
||||||
assert.Equal(t, expectedResult, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetVoucherList(t *testing.T) {
|
func TestGetVoucherList(t *testing.T) {
|
||||||
mockSubPrefixDb := new(mocks.MockSubPrefixDb)
|
mockSubPrefixDb := new(mocks.MockSubPrefixDb)
|
||||||
|
|
||||||
@ -2141,92 +2118,6 @@ func TestViewVoucher(t *testing.T) {
|
|||||||
mockSubPrefixDb.AssertExpectations(t)
|
mockSubPrefixDb.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetVoucherData(t *testing.T) {
|
|
||||||
mockSubPrefixDb := new(mocks.MockSubPrefixDb)
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Mocked voucher data
|
|
||||||
mockData := map[string][]byte{
|
|
||||||
"sym": []byte("1:SRF\n2:MILO"),
|
|
||||||
"bal": []byte("1:100\n2:200"),
|
|
||||||
"deci": []byte("1:6\n2:4"),
|
|
||||||
"addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock Get calls
|
|
||||||
for key, value := range mockData {
|
|
||||||
mockSubPrefixDb.On("Get", ctx, []byte(key)).Return(value, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := getVoucherData(ctx, mockSubPrefixDb, "1")
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "SRF", result.Symbol)
|
|
||||||
assert.Equal(t, "100", result.Balance)
|
|
||||||
assert.Equal(t, "6", result.Decimal)
|
|
||||||
assert.Equal(t, "0xd4c288865Ce", result.Address)
|
|
||||||
|
|
||||||
mockSubPrefixDb.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatchVoucher(t *testing.T) {
|
|
||||||
symbols := "1:SRF\n2:MILO"
|
|
||||||
balances := "1:100\n2:200"
|
|
||||||
decimals := "1:6\n2:4"
|
|
||||||
addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa"
|
|
||||||
|
|
||||||
// Test for valid voucher
|
|
||||||
symbol, balance, decimal, address := matchVoucher("2", symbols, balances, decimals, addresses)
|
|
||||||
|
|
||||||
// Assertions for valid voucher
|
|
||||||
assert.Equal(t, "MILO", symbol)
|
|
||||||
assert.Equal(t, "200", balance)
|
|
||||||
assert.Equal(t, "4", decimal)
|
|
||||||
assert.Equal(t, "0x41c188d63Qa", address)
|
|
||||||
|
|
||||||
// Test for non-existent voucher
|
|
||||||
symbol, balance, decimal, address = matchVoucher("3", symbols, balances, decimals, addresses)
|
|
||||||
|
|
||||||
// Assertions for non-match
|
|
||||||
assert.Equal(t, "", symbol)
|
|
||||||
assert.Equal(t, "", balance)
|
|
||||||
assert.Equal(t, "", decimal)
|
|
||||||
assert.Equal(t, "", address)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStoreTemporaryVoucher(t *testing.T) {
|
|
||||||
mockDataStore := new(mocks.MockUserDataStore)
|
|
||||||
ctx := context.Background()
|
|
||||||
sessionId := "session123"
|
|
||||||
|
|
||||||
voucherData := &VoucherMetadata{
|
|
||||||
Symbol: "SRF",
|
|
||||||
Balance: "200",
|
|
||||||
Decimal: "6",
|
|
||||||
Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define expected entries to be written
|
|
||||||
expectedEntries := map[utils.DataTyp][]byte{
|
|
||||||
utils.DATA_TEMPORARY_SYM: []byte("SRF"),
|
|
||||||
utils.DATA_TEMPORARY_BAL: []byte("200"),
|
|
||||||
utils.DATA_TEMPORARY_DECIMAL: []byte("6"),
|
|
||||||
utils.DATA_TEMPORARY_ADDRESS: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock WriteEntry calls
|
|
||||||
for key, value := range expectedEntries {
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
h := &Handlers{userdataStore: mockDataStore}
|
|
||||||
|
|
||||||
err := h.storeTemporaryVoucher(ctx, sessionId, voucherData)
|
|
||||||
|
|
||||||
assert.NoError(t, err)
|
|
||||||
mockDataStore.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetVoucher(t *testing.T) {
|
func TestSetVoucher(t *testing.T) {
|
||||||
mockDataStore := new(mocks.MockUserDataStore)
|
mockDataStore := new(mocks.MockUserDataStore)
|
||||||
|
|
||||||
@ -2285,63 +2176,3 @@ func TestSetVoucher(t *testing.T) {
|
|||||||
|
|
||||||
mockDataStore.AssertExpectations(t)
|
mockDataStore.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTemporaryVoucherData(t *testing.T) {
|
|
||||||
mockDataStore := new(mocks.MockUserDataStore)
|
|
||||||
sessionId := "session123"
|
|
||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
|
||||||
|
|
||||||
h := &Handlers{userdataStore: mockDataStore}
|
|
||||||
|
|
||||||
// Mock temporary voucher data
|
|
||||||
tempData := &VoucherMetadata{
|
|
||||||
Symbol: "SRF",
|
|
||||||
Balance: "100",
|
|
||||||
Decimal: "6",
|
|
||||||
Address: "0xd4c288865Ce",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up mocks for reading entries
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.Symbol), nil)
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil)
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.Decimal), nil)
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.Address), nil)
|
|
||||||
|
|
||||||
data, err := h.getTemporaryVoucherData(ctx, sessionId)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, tempData, data)
|
|
||||||
|
|
||||||
mockDataStore.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateVoucherData(t *testing.T) {
|
|
||||||
mockDataStore := new(mocks.MockUserDataStore)
|
|
||||||
ctx := context.Background()
|
|
||||||
sessionId := "session123"
|
|
||||||
|
|
||||||
h := &Handlers{userdataStore: mockDataStore}
|
|
||||||
|
|
||||||
data := &VoucherMetadata{
|
|
||||||
Symbol: "SRF",
|
|
||||||
Balance: "100",
|
|
||||||
Decimal: "6",
|
|
||||||
Address: "0xd4c288865Ce",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock WriteEntry for active data
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(data.Symbol)).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(data.Balance)).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_DECIMAL, []byte(data.Decimal)).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_ADDRESS, []byte(data.Address)).Return(nil)
|
|
||||||
|
|
||||||
// Mock WriteEntry for clearing temporary data
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL, []byte("")).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS, []byte("")).Return(nil)
|
|
||||||
|
|
||||||
err := h.updateVoucherData(ctx, sessionId, data)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
mockDataStore.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
184
internal/utils/vouchers.go
Normal file
184
internal/utils/vouchers.go
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VoucherMetadata helps organize voucher data fields
|
||||||
|
type VoucherMetadata struct {
|
||||||
|
Symbol string
|
||||||
|
Balance string
|
||||||
|
Decimal string
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessVouchers converts holdings into formatted strings
|
||||||
|
func ProcessVouchers(holdings []struct {
|
||||||
|
ContractAddress string `json:"contractAddress"`
|
||||||
|
TokenSymbol string `json:"tokenSymbol"`
|
||||||
|
TokenDecimals string `json:"tokenDecimals"`
|
||||||
|
Balance string `json:"balance"`
|
||||||
|
}) VoucherMetadata {
|
||||||
|
var data VoucherMetadata
|
||||||
|
var symbols, balances, decimals, addresses []string
|
||||||
|
|
||||||
|
for i, h := range holdings {
|
||||||
|
symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol))
|
||||||
|
balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance))
|
||||||
|
decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals))
|
||||||
|
addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Symbol = strings.Join(symbols, "\n")
|
||||||
|
data.Balance = strings.Join(balances, "\n")
|
||||||
|
data.Decimal = strings.Join(decimals, "\n")
|
||||||
|
data.Address = strings.Join(addresses, "\n")
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVoucherData retrieves and matches voucher data
|
||||||
|
func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) {
|
||||||
|
keys := []string{"sym", "bal", "deci", "addr"}
|
||||||
|
data := make(map[string]string)
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
value, err := db.Get(ctx, []byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get %s: %v", key, err)
|
||||||
|
}
|
||||||
|
data[key] = string(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol, balance, decimal, address := MatchVoucher(input,
|
||||||
|
data["sym"],
|
||||||
|
data["bal"],
|
||||||
|
data["deci"],
|
||||||
|
data["addr"])
|
||||||
|
|
||||||
|
if symbol == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &VoucherMetadata{
|
||||||
|
Symbol: symbol,
|
||||||
|
Balance: balance,
|
||||||
|
Decimal: decimal,
|
||||||
|
Address: address,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input.
|
||||||
|
func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) {
|
||||||
|
symList := strings.Split(symbols, "\n")
|
||||||
|
balList := strings.Split(balances, "\n")
|
||||||
|
decList := strings.Split(decimals, "\n")
|
||||||
|
addrList := strings.Split(addresses, "\n")
|
||||||
|
|
||||||
|
for i, sym := range symList {
|
||||||
|
parts := strings.SplitN(sym, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if input == parts[0] || strings.EqualFold(input, parts[1]) {
|
||||||
|
symbol = parts[1]
|
||||||
|
if i < len(balList) {
|
||||||
|
balance = strings.SplitN(balList[i], ":", 2)[1]
|
||||||
|
}
|
||||||
|
if i < len(decList) {
|
||||||
|
decimal = strings.SplitN(decList[i], ":", 2)[1]
|
||||||
|
}
|
||||||
|
if i < len(addrList) {
|
||||||
|
address = strings.SplitN(addrList[i], ":", 2)[1]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore.
|
||||||
|
func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error {
|
||||||
|
entries := map[DataTyp][]byte{
|
||||||
|
DATA_TEMPORARY_SYM: []byte(data.Symbol),
|
||||||
|
DATA_TEMPORARY_BAL: []byte(data.Balance),
|
||||||
|
DATA_TEMPORARY_DECIMAL: []byte(data.Decimal),
|
||||||
|
DATA_TEMPORARY_ADDRESS: []byte(data.Address),
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range entries {
|
||||||
|
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore.
|
||||||
|
func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*VoucherMetadata, error) {
|
||||||
|
keys := []DataTyp{
|
||||||
|
DATA_TEMPORARY_SYM,
|
||||||
|
DATA_TEMPORARY_BAL,
|
||||||
|
DATA_TEMPORARY_DECIMAL,
|
||||||
|
DATA_TEMPORARY_ADDRESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &VoucherMetadata{}
|
||||||
|
values := make([][]byte, len(keys))
|
||||||
|
|
||||||
|
for i, key := range keys {
|
||||||
|
value, err := store.ReadEntry(ctx, sessionId, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Symbol = string(values[0])
|
||||||
|
data.Balance = string(values[1])
|
||||||
|
data.Decimal = string(values[2])
|
||||||
|
data.Address = string(values[3])
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore.
|
||||||
|
func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error {
|
||||||
|
// Active voucher data entries
|
||||||
|
activeEntries := map[DataTyp][]byte{
|
||||||
|
DATA_ACTIVE_SYM: []byte(data.Symbol),
|
||||||
|
DATA_ACTIVE_BAL: []byte(data.Balance),
|
||||||
|
DATA_ACTIVE_DECIMAL: []byte(data.Decimal),
|
||||||
|
DATA_ACTIVE_ADDRESS: []byte(data.Address),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear temporary voucher data entries
|
||||||
|
tempEntries := map[DataTyp][]byte{
|
||||||
|
DATA_TEMPORARY_SYM: []byte(""),
|
||||||
|
DATA_TEMPORARY_BAL: []byte(""),
|
||||||
|
DATA_TEMPORARY_DECIMAL: []byte(""),
|
||||||
|
DATA_TEMPORARY_ADDRESS: []byte(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write active data
|
||||||
|
for key, value := range activeEntries {
|
||||||
|
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear temporary data
|
||||||
|
for key, value := range tempEntries {
|
||||||
|
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
240
internal/utils/vouchers_test.go
Normal file
240
internal/utils/vouchers_test.go
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
"github.com/alecthomas/assert/v2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
memdb "git.defalsify.org/vise.git/db/mem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssertEmptyValue checks if a value is empty/nil/zero
|
||||||
|
func AssertEmptyValue(t *testing.T, value []byte, msgAndArgs ...interface{}) {
|
||||||
|
assert.Equal(t, len(value), 0, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchVoucher(t *testing.T) {
|
||||||
|
symbols := "1:SRF\n2:MILO"
|
||||||
|
balances := "1:100\n2:200"
|
||||||
|
decimals := "1:6\n2:4"
|
||||||
|
addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa"
|
||||||
|
|
||||||
|
// Test for valid voucher
|
||||||
|
symbol, balance, decimal, address := MatchVoucher("2", symbols, balances, decimals, addresses)
|
||||||
|
|
||||||
|
// Assertions for valid voucher
|
||||||
|
assert.Equal(t, "MILO", symbol)
|
||||||
|
assert.Equal(t, "200", balance)
|
||||||
|
assert.Equal(t, "4", decimal)
|
||||||
|
assert.Equal(t, "0x41c188d63Qa", address)
|
||||||
|
|
||||||
|
// Test for non-existent voucher
|
||||||
|
symbol, balance, decimal, address = MatchVoucher("3", symbols, balances, decimals, addresses)
|
||||||
|
|
||||||
|
// Assertions for non-match
|
||||||
|
assert.Equal(t, "", symbol)
|
||||||
|
assert.Equal(t, "", balance)
|
||||||
|
assert.Equal(t, "", decimal)
|
||||||
|
assert.Equal(t, "", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessVouchers(t *testing.T) {
|
||||||
|
holdings := []struct {
|
||||||
|
ContractAddress string `json:"contractAddress"`
|
||||||
|
TokenSymbol string `json:"tokenSymbol"`
|
||||||
|
TokenDecimals string `json:"tokenDecimals"`
|
||||||
|
Balance string `json:"balance"`
|
||||||
|
}{
|
||||||
|
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
|
||||||
|
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedResult := VoucherMetadata{
|
||||||
|
Symbol: "1:SRF\n2:MILO",
|
||||||
|
Balance: "1:100\n2:200",
|
||||||
|
Decimal: "1:6\n2:4",
|
||||||
|
Address: "1:0xd4c288865Ce\n2:0x41c188d63Qa",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ProcessVouchers(holdings)
|
||||||
|
|
||||||
|
assert.Equal(t, expectedResult, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetVoucherData(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db := memdb.NewMemDb()
|
||||||
|
err := db.Connect(ctx, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
spdb := storage.NewSubPrefixDb(db, []byte("vouchers"))
|
||||||
|
|
||||||
|
// Test voucher data
|
||||||
|
mockData := map[string][]byte{
|
||||||
|
"sym": []byte("1:SRF\n2:MILO"),
|
||||||
|
"bal": []byte("1:100\n2:200"),
|
||||||
|
"deci": []byte("1:6\n2:4"),
|
||||||
|
"addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the data
|
||||||
|
for key, value := range mockData {
|
||||||
|
err = spdb.Put(ctx, []byte(key), []byte(value))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := GetVoucherData(ctx, spdb, "1")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "SRF", result.Symbol)
|
||||||
|
assert.Equal(t, "100", result.Balance)
|
||||||
|
assert.Equal(t, "6", result.Decimal)
|
||||||
|
assert.Equal(t, "0xd4c288865Ce", result.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreTemporaryVoucher(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
sessionId := "session123"
|
||||||
|
|
||||||
|
// Initialize memDb
|
||||||
|
db := memdb.NewMemDb()
|
||||||
|
err := db.Connect(ctx, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Create UserDataStore with memDb
|
||||||
|
store := &UserDataStore{
|
||||||
|
Db: db,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test data
|
||||||
|
voucherData := &VoucherMetadata{
|
||||||
|
Symbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
Decimal: "6",
|
||||||
|
Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the function being tested
|
||||||
|
err = StoreTemporaryVoucher(ctx, store, sessionId, voucherData)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify stored data
|
||||||
|
expectedEntries := map[DataTyp][]byte{
|
||||||
|
DATA_TEMPORARY_SYM: []byte("SRF"),
|
||||||
|
DATA_TEMPORARY_BAL: []byte("200"),
|
||||||
|
DATA_TEMPORARY_DECIMAL: []byte("6"),
|
||||||
|
DATA_TEMPORARY_ADDRESS: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, expectedValue := range expectedEntries {
|
||||||
|
storedValue, err := store.ReadEntry(ctx, sessionId, key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedValue, storedValue, "Mismatch for key %v", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTemporaryVoucherData(t *testing.T) {
|
||||||
|
sessionId := "session123"
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
// Initialize memDb
|
||||||
|
db := memdb.NewMemDb()
|
||||||
|
err := db.Connect(ctx, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Create UserDataStore with memDb
|
||||||
|
store := &UserDataStore{
|
||||||
|
Db: db,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test voucher data
|
||||||
|
tempData := &VoucherMetadata{
|
||||||
|
Symbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
Decimal: "6",
|
||||||
|
Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the data
|
||||||
|
err = StoreTemporaryVoucher(ctx, store, sessionId, tempData)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Execute the function being tested
|
||||||
|
data, err := GetTemporaryVoucherData(ctx, store, sessionId)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tempData, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateVoucherData(t *testing.T) {
|
||||||
|
sessionId := "session123"
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
// Initialize memDb
|
||||||
|
db := memdb.NewMemDb()
|
||||||
|
err := db.Connect(ctx, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
store := &UserDataStore{
|
||||||
|
Db: db,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test data
|
||||||
|
data := &VoucherMetadata{
|
||||||
|
Symbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
Decimal: "6",
|
||||||
|
Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// First store some temporary data to verify it gets cleared
|
||||||
|
tempData := &VoucherMetadata{
|
||||||
|
Symbol: "OLD",
|
||||||
|
Balance: "100",
|
||||||
|
Decimal: "8",
|
||||||
|
Address: "0xold",
|
||||||
|
}
|
||||||
|
err = StoreTemporaryVoucher(ctx, store, sessionId, tempData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Execute update
|
||||||
|
err = UpdateVoucherData(ctx, store, sessionId, data)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify active data was stored correctly
|
||||||
|
activeEntries := map[DataTyp][]byte{
|
||||||
|
DATA_ACTIVE_SYM: []byte(data.Symbol),
|
||||||
|
DATA_ACTIVE_BAL: []byte(data.Balance),
|
||||||
|
DATA_ACTIVE_DECIMAL: []byte(data.Decimal),
|
||||||
|
DATA_ACTIVE_ADDRESS: []byte(data.Address),
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, expectedValue := range activeEntries {
|
||||||
|
storedValue, err := store.ReadEntry(ctx, sessionId, key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify temporary data was cleared
|
||||||
|
tempKeys := []DataTyp{
|
||||||
|
DATA_TEMPORARY_SYM,
|
||||||
|
DATA_TEMPORARY_BAL,
|
||||||
|
DATA_TEMPORARY_DECIMAL,
|
||||||
|
DATA_TEMPORARY_ADDRESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range tempKeys {
|
||||||
|
storedValue, err := store.ReadEntry(ctx, sessionId, key)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
AssertEmptyValue(t, storedValue, "Temporary data not cleared for key %v", key)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user