portfolio: Fix CryptoID balance issue and assertify tests (#1861)

* portfolio: Fix CryptoID balance issue and assertify tests

* portfolio: Expand context usage, enhance tests and a few other minor improvements

* portfolio: Further improvements and enhance common.IsValidCryptoAddress

* config, portfolio: Use v6.DefaultConfig, switch to context.WithCancel
This commit is contained in:
Adrian Gallagher
2025-03-28 12:41:01 +11:00
committed by GitHub
parent 06afde1460
commit cc05f7e6fd
16 changed files with 723 additions and 845 deletions

View File

@@ -54,23 +54,24 @@ var (
// Public common Errors
var (
ErrNotYetImplemented = errors.New("not yet implemented")
ErrFunctionNotSupported = errors.New("unsupported wrapper function")
errInvalidCryptoCurrency = errors.New("invalid crypto currency")
ErrDateUnset = errors.New("date unset")
ErrStartAfterEnd = errors.New("start date after end date")
ErrStartEqualsEnd = errors.New("start date equals end date")
ErrStartAfterTimeNow = errors.New("start date is after current time")
ErrNilPointer = errors.New("nil pointer")
ErrEmptyParams = errors.New("empty parameters")
ErrCannotCalculateOffline = errors.New("cannot calculate offline, unsupported")
ErrNoResponse = errors.New("no response")
ErrTypeAssertFailure = errors.New("type assert failure")
ErrNoResults = errors.New("no results found")
ErrUnknownError = errors.New("unknown error")
ErrGettingField = errors.New("error getting field")
ErrSettingField = errors.New("error setting field")
ErrParsingWSField = errors.New("error parsing websocket field")
ErrNotYetImplemented = errors.New("not yet implemented")
ErrFunctionNotSupported = errors.New("unsupported wrapper function")
ErrAddressIsEmptyOrInvalid = errors.New("address is empty or invalid")
ErrUnsupportedCryptocurrency = errors.New("unsupported cryptocurrency") // TODO: Remove me, used because of an import cycle if we use the currency package
ErrDateUnset = errors.New("date unset")
ErrStartAfterEnd = errors.New("start date after end date")
ErrStartEqualsEnd = errors.New("start date equals end date")
ErrStartAfterTimeNow = errors.New("start date is after current time")
ErrNilPointer = errors.New("nil pointer")
ErrEmptyParams = errors.New("empty parameters")
ErrCannotCalculateOffline = errors.New("cannot calculate offline, unsupported")
ErrNoResponse = errors.New("no response")
ErrTypeAssertFailure = errors.New("type assert failure")
ErrNoResults = errors.New("no results found")
ErrUnknownError = errors.New("unknown error")
ErrGettingField = errors.New("error getting field")
ErrSettingField = errors.New("error setting field")
ErrParsingWSField = errors.New("error parsing websocket field")
)
var (
@@ -190,17 +191,30 @@ func IsEnabled(isEnabled bool) string {
// IsValidCryptoAddress validates your cryptocurrency address string using the
// regexp package // Validation issues occurring because "3" is contained in
// litecoin and Bitcoin addresses - non-fatal
func IsValidCryptoAddress(address, crypto string) (bool, error) {
func IsValidCryptoAddress(address, crypto string) error {
var matched bool
var err error
switch strings.ToLower(crypto) {
case "btc":
return regexp.MatchString("^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,90}$", address)
matched, err = regexp.MatchString("^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,90}$", address)
case "ltc":
return regexp.MatchString("^[L3M][a-km-zA-HJ-NP-Z1-9]{25,34}$", address)
matched, err = regexp.MatchString("^[L3M][a-km-zA-HJ-NP-Z1-9]{25,34}$", address)
case "eth":
return regexp.MatchString("^0x[a-km-z0-9]{40}$", address)
matched, err = regexp.MatchString("^0x[a-km-z0-9]{40}$", address)
default:
return false, fmt.Errorf("%w %s", errInvalidCryptoCurrency, crypto)
return fmt.Errorf("%w: %q", ErrUnsupportedCryptocurrency, crypto)
}
if err != nil {
return err
}
if !matched {
return fmt.Errorf("%w: %q", ErrAddressIsEmptyOrInvalid, address)
}
return nil
}
// YesOrNo returns a boolean variable to check if input is "y" or "yes"

View File

@@ -137,101 +137,30 @@ func TestIsEnabled(t *testing.T) {
func TestIsValidCryptoAddress(t *testing.T) {
t.Parallel()
b, err := IsValidCryptoAddress("1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "bTC")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if !b {
t.Errorf("expected address '%s' to be valid", "1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX")
tests := []struct {
name, addr, code string
err error
}{
{"Valid BTC legacy", "1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "bTC", nil},
{"Valid BTC bech32", "bc1qw508d6qejxtdg4y5r3zarvaly0c5xw7kv8f3t4", "bTC", nil},
{"Invalid BTC (too long)", "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", "bTC", ErrAddressIsEmptyOrInvalid},
{"Valid BTC bech32 (longer)", "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9", "bTC", nil},
{"Invalid BTC (starts with 0)", "0Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "bTC", ErrAddressIsEmptyOrInvalid},
{"Invalid LTC (BTC address)", "1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "lTc", ErrAddressIsEmptyOrInvalid},
{"Valid LTC", "3CDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", "lTc", nil},
{"Invalid LTC (starts with N)", "NCDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", "lTc", ErrAddressIsEmptyOrInvalid},
{"Valid ETH", "0xb794f5ea0ba39494ce839613fffba74279579268", "eth", nil},
{"Invalid ETH (starts with xx)", "xxb794f5ea0ba39494ce839613fffba74279579268", "eth", ErrAddressIsEmptyOrInvalid},
{"Unsupported crypto", "xxb794f5ea0ba39494ce839613fffba74279579268", "wif", ErrUnsupportedCryptocurrency},
{"Empty address", "", "btc", ErrAddressIsEmptyOrInvalid},
}
b, err = IsValidCryptoAddress("bc1qw508d6qejxtdg4y5r3zarvaly0c5xw7kv8f3t4", "bTC")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if !b {
t.Errorf("expected address '%s' to be valid", "bc1qw508d6qejxtdg4y5r3zarvaly0c5xw7kv8f3t4")
}
b, err = IsValidCryptoAddress("an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", "bTC")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if b {
t.Errorf("expected address '%s' to be invalid", "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx")
}
b, err = IsValidCryptoAddress("bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9", "bTC")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if !b {
t.Errorf("expected address '%s' to be valid", "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9")
}
b, err = IsValidCryptoAddress("0Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "btc")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if b {
t.Errorf("expected address '%s' to be invalid", "0Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX")
}
b, err = IsValidCryptoAddress("1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX", "lTc")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if b {
t.Errorf("expected address '%s' to be invalid", "1Mz7153HMuxXTuR2R1t78mGSdzaAtNbBWX")
}
b, err = IsValidCryptoAddress("3CDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", "ltc")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if !b {
t.Errorf("expected address '%s' to be valid", "3CDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj")
}
b, err = IsValidCryptoAddress("NCDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", "lTc")
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if b {
t.Errorf("expected address '%s' to be invalid", "NCDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj")
}
b, err = IsValidCryptoAddress(
"0xb794f5ea0ba39494ce839613fffba74279579268",
"eth",
)
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if !b {
t.Errorf("expected address '%s' to be valid", "0xb794f5ea0ba39494ce839613fffba74279579268")
}
b, err = IsValidCryptoAddress(
"xxb794f5ea0ba39494ce839613fffba74279579268",
"eTh",
)
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if b {
t.Errorf("expected address '%s' to be invalid", "xxb794f5ea0ba39494ce839613fffba74279579268")
}
b, err = IsValidCryptoAddress(
"xxb794f5ea0ba39494ce839613fffba74279579268",
"ding",
)
if !errors.Is(err, errInvalidCryptoCurrency) {
t.Errorf("received '%v' expected '%v'", err, errInvalidCryptoCurrency)
}
if b {
t.Errorf("expected address '%s' to be invalid", "xxb794f5ea0ba39494ce839613fffba74279579268")
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
require.ErrorIs(t, IsValidCryptoAddress(tc.addr, tc.code), tc.err)
})
}
}