Skip to content
Draft
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
166 changes: 166 additions & 0 deletions cmd/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"math"
"reflect"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -120,6 +121,24 @@ func TestFeeToShares(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 TestGetTransferAmountNAVAX(t *testing.T) {
origAmount := transferAmount
origAmountNAVAX := transferAmountNAVAX
Expand Down Expand Up @@ -202,6 +221,153 @@ func TestParseTimeRange(t *testing.T) {
}
}

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)
}

_, err = parseAutoRenewPeriod("0s")
if err == nil {
t.Fatal("parseAutoRenewPeriod() expected error for zero period")
}

_, err = parseAutoRenewPeriod("1.5s")
if err == nil {
t.Fatal("parseAutoRenewPeriod() expected error for sub-second period")
}

_, err = parseAutoRenewPeriod("bad-period")
if err == nil {
t.Fatal("parseAutoRenewPeriod() expected error for invalid period")
}
}

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 TestParseRewardAutoTimestamp(t *testing.T) {
tests := []struct {
name string
input string
wantUnix uint64
wantRFC string
wantError bool
}{
{
name: "RFC3339",
input: "2026-06-04T12:30:00Z",
wantUnix: 1780576200,
wantRFC: "2026-06-04T12:30:00Z",
},
{
name: "Unix seconds",
input: "1780576200",
wantUnix: 1780576200,
wantRFC: "2026-06-04T12:30:00Z",
},
{
name: "empty",
input: "",
wantError: true,
},
{
name: "zero",
input: "0",
wantError: true,
},
{
name: "too large",
input: "9223372036854775808",
wantError: true,
},
{
name: "invalid",
input: "bad-timestamp",
wantError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotUnix, gotTime, err := parseRewardAutoTimestamp(tt.input)
if tt.wantError {
if err == nil {
t.Fatalf("parseRewardAutoTimestamp(%q) expected error", tt.input)
}
return
}
if err != nil {
t.Fatalf("parseRewardAutoTimestamp(%q) returned error: %v", tt.input, err)
}
if gotUnix != tt.wantUnix {
t.Fatalf("parseRewardAutoTimestamp(%q) unix = %d, want %d", tt.input, gotUnix, tt.wantUnix)
}
if gotTime.Format(time.RFC3339) != tt.wantRFC {
t.Fatalf("parseRewardAutoTimestamp(%q) time = %s, want %s", tt.input, gotTime.Format(time.RFC3339), tt.wantRFC)
}
})
}
}

func TestParseValidatorAddrs(t *testing.T) {
got := parseValidatorAddrs(" 127.0.0.1 , node.example.com:9650 ,,https://node.example.com ")
want := []string{"127.0.0.1", "node.example.com:9650", "https://node.example.com"}
Expand Down
12 changes: 8 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,17 @@ func avaxToNAVAX(avax float64) (uint64, error) {
return uint64(math.Round(avax * 1e9)), nil
}

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 * 1_000_000)), 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