Skip to content

Commit

Permalink
feat: voucher prototype (#279)
Browse files Browse the repository at this point in the history
Co-authored-by: Mohammad Hasan Saeedkia <[email protected]>
  • Loading branch information
sepehr-dh99 and MHSaeedkia authored Jan 21, 2025
1 parent 7c53c69 commit 06e9cd5
Show file tree
Hide file tree
Showing 15 changed files with 371 additions and 178 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ build-http:
gen:
go run ./internal/generator/main.go \
"./internal/engine/command/crowdfund/crowdfund.yml" \
"./internal/engine/command/voucher/voucher.yml" \
"./internal/engine/command/market/market.yml"

find . -name "*.gen.go" -exec gofumpt -l -w {} +
Expand Down
26 changes: 13 additions & 13 deletions internal/engine/command/voucher/claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,51 @@ func (v *VoucherCmd) claimHandler(
) command.CommandResult {
code := args["code"]
if len(code) != 8 {
return cmd.ErrorResult(errors.New("voucher code is not valid, length must be 8"))
return cmd.RenderFailedTemplate("Voucher code is not valid, length must be 8")
}

voucher, err := v.db.GetVoucherByCode(code)
if err != nil {
return cmd.ErrorResult(errors.New("voucher code is not valid, no voucher found"))
return cmd.RenderFailedTemplate("Voucher code is not valid, no voucher found")
}

if voucher.CreatedAt.AddDate(0, int(voucher.ValidMonths), 0).Before(time.Now()) {
return cmd.ErrorResult(errors.New("voucher is expired"))
return cmd.RenderFailedTemplate("Voucher is expired")
}

if voucher.IsClaimed() {
return cmd.ErrorResult(errors.New("voucher code claimed before"))
return cmd.RenderFailedTemplate("Voucher code claimed before")
}

address := args["address"]
valInfo, _ := v.clientManager.GetValidatorInfo(address)
if valInfo != nil {
err = errors.New("this address is already a staked validator")
log.Warn(fmt.Sprintf("staked validator found. %s", address))
log.Warn(fmt.Sprintf("Staked validator found. %s", address))

return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

pubKey, err := v.clientManager.FindPublicKey(address, false)
if err != nil {
log.Warn(fmt.Sprintf("peer not found. %s", address))
log.Warn(fmt.Sprintf("Peer not found. %s", address))

return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

memo := fmt.Sprintf("voucher %s claimed by Pagu", code)
memo := fmt.Sprintf("Voucher %s claimed by Pagu", code)
txHash, err := v.wallet.BondTransaction(pubKey, address, memo, voucher.Amount)
if err != nil {
return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

if txHash == "" {
return cmd.ErrorResult(errors.New("can't send bond transaction"))
return cmd.RenderFailedTemplate("Can't send bond transaction")
}

if err = v.db.ClaimVoucher(voucher.ID, txHash, caller.ID); err != nil {
return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

return cmd.SuccessfulResultF("Voucher claimed successfully!\n\n https://pacviewer.com/transaction/%s", txHash)
return cmd.RenderResultTemplate("txHash", txHash)
}
20 changes: 9 additions & 11 deletions internal/engine/command/voucher/claim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package voucher
import (
"testing"

"github.com/pagu-project/pagu/internal/engine/command"
"github.com/pagu-project/pagu/internal/entity"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
Expand All @@ -14,26 +13,25 @@ func TestClaim(t *testing.T) {

voucherCode := "12345678"
caller := &entity.User{DBModel: entity.DBModel{ID: 1}}
cmd := &command.Command{}

t.Run("Invalid Voucher Code", func(t *testing.T) {
args := map[string]string{
"code": "0",
"address": "pc1p...",
}
result := td.voucherCmd.claimHandler(caller, cmd, args)
result := td.voucherCmd.claimHandler(caller, td.voucherCmd.subCmdClaim, args)
assert.False(t, result.Successful)
assert.Equal(t, result.Message, "An error occurred: voucher code is not valid, length must be 8")
assert.Contains(t, result.Message, "Voucher code is not valid, length must be 8")
})

t.Run("Voucher Code Not Issued Yet", func(t *testing.T) {
args := map[string]string{
"code": voucherCode,
"address": "pc1p...",
}
result := td.voucherCmd.claimHandler(caller, cmd, args)
result := td.voucherCmd.claimHandler(caller, td.voucherCmd.subCmdClaim, args)
assert.False(t, result.Successful)
assert.Equal(t, result.Message, "An error occurred: voucher code is not valid, no voucher found")
assert.Contains(t, result.Message, "Voucher code is not valid, no voucher found")
})

t.Run("Claim a Voucher", func(t *testing.T) {
Expand All @@ -49,26 +47,26 @@ func TestClaim(t *testing.T) {
).AnyTimes()

td.mockWallet.EXPECT().BondTransaction(gomock.Any(), validatorAddr,
"voucher 12345678 claimed by Pagu", testVoucher.Amount).Return(
"Voucher 12345678 claimed by Pagu", testVoucher.Amount).Return(
"0x1", nil,
).AnyTimes()

args := map[string]string{
"code": voucherCode,
"address": validatorAddr,
}
result := td.voucherCmd.claimHandler(caller, cmd, args)
result := td.voucherCmd.claimHandler(caller, td.voucherCmd.subCmdClaim, args)
assert.True(t, result.Successful)
assert.Equal(t, result.Message, "Voucher claimed successfully!\n\n https://pacviewer.com/transaction/0x1")
assert.Contains(t, result.Message, "Voucher claimed successfully!\n\nhttps://pacviewer.com/transaction/0x1")
})

t.Run("Claim again", func(t *testing.T) {
args := map[string]string{
"code": voucherCode,
"address": "pc1p...",
}
result := td.voucherCmd.claimHandler(caller, cmd, args)
result := td.voucherCmd.claimHandler(caller, td.voucherCmd.subCmdClaim, args)
assert.False(t, result.Successful)
assert.Equal(t, result.Message, "An error occurred: voucher code claimed before")
assert.Contains(t, result.Message, "Voucher code claimed before")
})
}
30 changes: 15 additions & 15 deletions internal/engine/command/voucher/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ func (v *VoucherCmd) createHandler(

amt, err := amount.FromString(args["amount"])
if err != nil {
return cmd.ErrorResult(errors.New("invalid amount param"))
return cmd.RenderFailedTemplate("Invalid amount param")
}

maxStake, _ := amount.NewAmount(1000)
if amt > maxStake {
return cmd.ErrorResult(errors.New("stake amount is more than 1000"))
return cmd.RenderFailedTemplate("Stake amount is more than 1000")
}

expireMonths, err := strconv.Atoi(args["valid-months"])
if err != nil {
return cmd.ErrorResult(errors.New("invalid valid-months param"))
return cmd.RenderFailedTemplate("Invalid valid-months param")
}

vch := &entity.Voucher{
Expand All @@ -63,10 +63,10 @@ func (v *VoucherCmd) createHandler(

err = v.db.AddVoucher(vch)
if err != nil {
return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

return cmd.SuccessfulResultF("Voucher created successfully! \n Code: %s", vch.Code)
return cmd.RenderResultTemplate("voucher", vch)
}

func (v *VoucherCmd) createBulkHandler(
Expand All @@ -82,14 +82,14 @@ func (v *VoucherCmd) createBulkHandler(
if err != nil {
log.Error(err.Error())

return cmd.ErrorResult(errors.New("failed to fetch attachment content"))
return cmd.RenderFailedTemplate("Failed to fetch attachment content")
}

resp, err := httpClient.Do(req)
if err != nil {
log.Error(err.Error())

return cmd.ErrorResult(errors.New("failed to fetch attachment content"))
return cmd.RenderFailedTemplate("Failed to fetch attachment content")
}

defer func() {
Expand All @@ -101,7 +101,7 @@ func (v *VoucherCmd) createBulkHandler(
if err != nil {
log.Error(err.Error())

return cmd.ErrorResult(errors.New("failed to read csv content"))
return cmd.RenderFailedTemplate("Failed to read csv content")
}

var records []BulkRecorder
Expand All @@ -112,38 +112,38 @@ func (v *VoucherCmd) createBulkHandler(
} else if err != nil {
log.Error(err.Error())

return cmd.ErrorResult(errors.New("failed to parse csv content"))
return cmd.RenderFailedTemplate("Failed to parse csv content")
}

records = append(records, r)
}

if len(records) == 0 {
err = fmt.Errorf("no record founded. please add at least one record to csv file")
err = fmt.Errorf("no record founded. Please add at least one record to csv file")

return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

vouchers, err := v.createBulkVoucher(records, caller.ID)
if err != nil {
return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

for _, vch := range vouchers {
// TODO: add gorm transaction for this two insert
err := v.db.AddVoucher(vch)
if err != nil {
return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}

if notify == "TRUE" {
if v.createNotification(vch.Email, vch.Code, vch.Recipient, vch.Amount.ToPAC()) != nil {
return cmd.ErrorResult(err)
return cmd.RenderErrorTemplate(err)
}
}
}

return cmd.SuccessfulResult("Vouchers created successfully!")
return cmd.RenderResultTemplate()
}

func (v *VoucherCmd) createBulkVoucher(records []BulkRecorder, callerID uint) ([]*entity.Voucher, error) {
Expand Down
15 changes: 6 additions & 9 deletions internal/engine/command/voucher/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import (
"testing"

"github.com/h2non/gock"
"github.com/pagu-project/pagu/internal/engine/command"
"github.com/pagu-project/pagu/internal/entity"
"github.com/stretchr/testify/assert"
)

func TestCreateOne(t *testing.T) {
td := setup(t)

cmd := &command.Command{}
caller := &entity.User{DBModel: entity.DBModel{ID: 1}}

t.Run("more than 1000 PAC", func(t *testing.T) {
Expand All @@ -21,9 +19,9 @@ func TestCreateOne(t *testing.T) {
"valid-months": "1",
}

result := td.voucherCmd.createHandler(caller, cmd, args)
result := td.voucherCmd.createHandler(caller, td.voucherCmd.subCmdCreate, args)
assert.False(t, result.Successful)
assert.Contains(t, result.Message, "stake amount is more than 1000")
assert.Contains(t, result.Message, "Stake amount is more than 1000")
})

t.Run("wrong month", func(t *testing.T) {
Expand All @@ -32,7 +30,7 @@ func TestCreateOne(t *testing.T) {
"valid-months": "1.1",
}

result := td.voucherCmd.createHandler(caller, cmd, args)
result := td.voucherCmd.createHandler(caller, td.voucherCmd.subCmdCreate, args)
assert.False(t, result.Successful)
})

Expand All @@ -42,7 +40,7 @@ func TestCreateOne(t *testing.T) {
"valid-months": "1",
}

result := td.voucherCmd.createHandler(caller, cmd, args)
result := td.voucherCmd.createHandler(caller, td.voucherCmd.subCmdCreate, args)
assert.True(t, result.Successful)
assert.Contains(t, result.Message, "Voucher created successfully!")
})
Expand All @@ -55,7 +53,7 @@ func TestCreateOne(t *testing.T) {
"description": "Testnet node",
}

result := td.voucherCmd.createHandler(caller, cmd, args)
result := td.voucherCmd.createHandler(caller, td.voucherCmd.subCmdCreate, args)
assert.True(t, result.Successful)
assert.Contains(t, result.Message, "Voucher created successfully!")
})
Expand All @@ -64,7 +62,6 @@ func TestCreateOne(t *testing.T) {
func TestCreateBulk(t *testing.T) {
td := setup(t)

cmd := &command.Command{}
caller := &entity.User{DBModel: entity.DBModel{ID: 1}}

t.Run("normal", func(t *testing.T) {
Expand All @@ -81,7 +78,7 @@ func TestCreateBulk(t *testing.T) {
"notify": "TRUE",
}

result := td.voucherCmd.createBulkHandler(caller, cmd, args)
result := td.voucherCmd.createBulkHandler(caller, td.voucherCmd.subCmdCreateBulk, args)

assert.True(t, result.Successful)
assert.Contains(t, result.Message, "Vouchers created successfully!")
Expand Down
18 changes: 9 additions & 9 deletions internal/engine/command/voucher/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (v *VoucherCmd) statusHandler(_ *entity.User, cmd *command.Command, args ma
func (v *VoucherCmd) statusVoucher(cmd *command.Command, code string) command.CommandResult {
voucher, err := v.db.GetVoucherByCode(code)
if err != nil {
return cmd.ErrorResult(errors.New("voucher code is not valid, no voucher found"))
return cmd.RenderFailedTemplate("Voucher code is not valid, no voucher found")
}

isClaimed := "NO"
Expand All @@ -36,15 +36,15 @@ func (v *VoucherCmd) statusVoucher(cmd *command.Command, code string) command.Co
txLink = fmt.Sprintf("https://pacviewer.com/transaction/%s", voucher.TxHash)
}

return cmd.SuccessfulResultF("Code: %s\nAmount: %s\n"+
"Expire At: %s\nRecipient: %s\nDescription: %s\nClaimed: %v\nTx Link: %s"+
"\n",
voucher.Code,
voucher.Amount,
voucher.CreatedAt.AddDate(0, int(voucher.ValidMonths), 0).Format("02/01/2006, 15:04:05"),
voucher.Recipient,
voucher.Desc,
voucherExpiryDate := voucher.CreatedAt.AddDate(0, int(voucher.ValidMonths), 0).Format("02/01/2006, 15:04:05")

return cmd.RenderResultTemplate("voucher",
voucher,
"expireAt",
voucherExpiryDate,
"isClaimed",
isClaimed,
"txLink",
txLink)
}

Expand Down
Loading

0 comments on commit 06e9cd5

Please sign in to comment.