Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions cmd/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"math"
"strings"
"testing"
"time"
)
Expand Down Expand Up @@ -194,6 +195,74 @@ func TestParseTimeRange(t *testing.T) {
}
}

func TestFractionToShares(t *testing.T) {
got, err := fractionToShares("auto-compound", 0.3)
if err != nil {
t.Fatalf("fractionToShares() returned error: %v", err)
}
if got != 300_000 {
t.Fatalf("fractionToShares() = %d, want 300000", got)
}

_, err = fractionToShares("auto-compound", 1.01)
if err == nil {
t.Fatal("fractionToShares() expected error for value above one")
}
if !strings.Contains(err.Error(), "auto-compound") {
t.Fatalf("fractionToShares() error = %v, want field name", err)
}
}

func TestParseAutoRenewPeriod(t *testing.T) {
got, err := parseAutoRenewPeriod("336h")
if err != nil {
t.Fatalf("parseAutoRenewPeriod() returned error: %v", err)
}
if got != 14*24*time.Hour {
t.Fatalf("parseAutoRenewPeriod() = %s, want 336h", got)
}

for _, bad := range []string{"0s", "1.5s", "bad-period"} {
if _, err := parseAutoRenewPeriod(bad); err == nil {
t.Fatalf("parseAutoRenewPeriod(%q) expected error", bad)
}
}
}

func TestParseAutoRenewConfigPeriod(t *testing.T) {
tests := []struct {
name string
input string
want time.Duration
wantErr bool
}{
{name: "zero duration", input: "0", want: 0},
{name: "zero seconds", input: "0s", want: 0},
{name: "non-zero duration", input: "336h", want: 14 * 24 * time.Hour},
{name: "negative duration", input: "-1s", wantErr: true},
{name: "sub-second duration", input: "1.5s", wantErr: true},
{name: "invalid duration", input: "bad-period", wantErr: true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseAutoRenewConfigPeriod(tt.input)
if tt.wantErr {
if err == nil {
t.Fatalf("parseAutoRenewConfigPeriod(%q) expected error", tt.input)
}
return
}
if err != nil {
t.Fatalf("parseAutoRenewConfigPeriod(%q) returned error: %v", tt.input, err)
}
if got != tt.want {
t.Fatalf("parseAutoRenewConfigPeriod(%q) = %s, want %s", tt.input, got, tt.want)
}
})
}
}

func TestNormalizeValidatorNodeURI(t *testing.T) {
tests := []struct {
name string
Expand Down
16 changes: 12 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"syscall"
"time"

"github.com/ava-labs/avalanchego/vms/platformvm/reward"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -95,13 +96,20 @@ func avaxToNAVAX(avax float64) (uint64, error) {
return uint64(math.Round(avax * 1e9)), nil
}

// fractionToShares converts a decimal fraction (0.02 = 2%) to shares out of
// reward.PercentDenominator (0.02 -> 20,000). Uses rounding to avoid float
// precision issues. [name] is used in the error message.
func fractionToShares(name string, value float64) (uint32, error) {
if value < 0 || value > 1 {
return 0, fmt.Errorf("%s must be between 0 and 1 (got %.4f)", name, value)
}
return uint32(math.Round(value * reward.PercentDenominator)), nil
}

// feeToShares converts a decimal fee (0.02 = 2%) to shares (20,000 out of 1,000,000).
// Uses rounding to avoid float precision issues.
func feeToShares(fee float64) (uint32, error) {
if fee < 0 || fee > 1 {
return 0, fmt.Errorf("delegation fee must be between 0 and 1 (got %.4f)", fee)
}
return uint32(math.Round(fee * 1_000_000)), nil
return fractionToShares("delegation fee", fee)
}

// getOperationContext returns a context with timeout and signal handling.
Expand Down
Loading
Loading