invoice: add unit tests for each parsed tagged field
This commit introduces unit tests for each parsed tagged field to cover all possible input scenarios.
This commit is contained in:
2 files changed
+675
-176
No files matched your search
@@ -1,9 +1,16 @@
|
||||
package zpay32
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg"
|
||||
"github.com/roasbeef/btcutil"
|
||||
"github.com/roasbeef/btcutil/bech32"
|
||||
)
|
||||
|
||||
// TestDecodeAmount ensures that the amount string in the hrp of the Invoice
|
||||
@@ -118,11 +125,11 @@ func TestDecodeAmount(t *testing.T) {
|
||||
for i, test := range tests {
|
||||
sat, err := decodeAmount(test.amount)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("Amount decoding test %d failed: %v", i, err)
|
||||
t.Errorf("amount decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && sat != test.result {
|
||||
t.Fatalf("%d) failed decoding amount, expected %v, "+
|
||||
t.Fatalf("test %d failed decoding amount, expected %v, "+
|
||||
"got %v", i, test.result, sat)
|
||||
}
|
||||
}
|
||||
@@ -193,11 +200,11 @@ func TestEncodeAmount(t *testing.T) {
|
||||
for i, test := range tests {
|
||||
shortened, err := encodeAmount(test.msat)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("Amount encoding test %d failed: %v", i, err)
|
||||
t.Errorf("amount encoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && shortened != test.result {
|
||||
t.Fatalf("%d) failed encoding amount, expected %v, "+
|
||||
t.Fatalf("test %d failed encoding amount, expected %v, "+
|
||||
"got %v", i, test.result, shortened)
|
||||
}
|
||||
}
|
||||
@@ -230,13 +237,505 @@ func TestParseTimestamp(t *testing.T) {
|
||||
for i, test := range tests {
|
||||
time, err := parseTimestamp(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("Data decoding test %d failed: %v", i, err)
|
||||
t.Errorf("timestamp decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && time != test.result {
|
||||
t.Errorf("Timestamp decoding test %d failed: expected "+
|
||||
"timestamp %d, got %d", i, test.result, time)
|
||||
t.Fatalf("test %d failed decoding timestamp: "+
|
||||
"expected %d, got %d",
|
||||
i, test.result, time)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseFieldDataLength checks that the 16 bit length is properly parsed.
|
||||
func TestParseFieldDataLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result uint16
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: false, // empty data
|
||||
},
|
||||
{
|
||||
data: []byte{0x0},
|
||||
valid: false, // data too short
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x0, 0x0},
|
||||
valid: false, // data too long
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x0},
|
||||
valid: true,
|
||||
result: 0,
|
||||
},
|
||||
{
|
||||
data: []byte{0x1f, 0x1f},
|
||||
valid: true,
|
||||
result: 1023,
|
||||
},
|
||||
{
|
||||
// The first byte is <= 3 bits long.
|
||||
data: []byte{0x1, 0x2},
|
||||
valid: true,
|
||||
result: 34,
|
||||
},
|
||||
{
|
||||
// The first byte is > 3 bits long.
|
||||
data: []byte{0xa, 0x0},
|
||||
valid: true,
|
||||
result: 320,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
length, err := parseFieldDataLength(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("field data length decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && length != test.result {
|
||||
t.Fatalf("test %d failed decoding field data length: "+
|
||||
"expected %d, got %d",
|
||||
i, test.result, length)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParsePaymentHash checks that the payment hash is properly parsed.
|
||||
// If the data does not have a length of 52 bytes, we skip over parsing the
|
||||
// field and do not return an error.
|
||||
func TestParsePaymentHash(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testPaymentHashData, _ := bech32.ConvertBits(testPaymentHash[:], 8, 5, true)
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result *[32]byte
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 52 bytes
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 52 bytes
|
||||
},
|
||||
{
|
||||
data: testPaymentHashData,
|
||||
valid: true,
|
||||
result: &testPaymentHash,
|
||||
},
|
||||
{
|
||||
data: append(testPaymentHashData, 0x0),
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 52 bytes
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
paymentHash, err := parsePaymentHash(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("payment hash decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && !compareHashes(paymentHash, test.result) {
|
||||
t.Fatalf("test %d failed decoding payment hash: "+
|
||||
"expected %x, got %x",
|
||||
i, *test.result, *paymentHash)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseDescription checks that the description is properly parsed.
|
||||
func TestParseDescription(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCupOfCoffeeData, _ := bech32.ConvertBits([]byte(testCupOfCoffee), 8, 5, true)
|
||||
testPleaseConsiderData, _ := bech32.ConvertBits([]byte(testPleaseConsider), 8, 5, true)
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result *string
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: &testEmptyString,
|
||||
},
|
||||
{
|
||||
data: testCupOfCoffeeData,
|
||||
valid: true,
|
||||
result: &testCupOfCoffee,
|
||||
},
|
||||
{
|
||||
data: testPleaseConsiderData,
|
||||
valid: true,
|
||||
result: &testPleaseConsider,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
description, err := parseDescription(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("description decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && !reflect.DeepEqual(description, test.result) {
|
||||
t.Fatalf("test %d failed decoding description: "+
|
||||
"expected \"%s\", got \"%s\"",
|
||||
i, *test.result, *description)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseDestination checks that the destination is properly parsed.
|
||||
// If the data does not have a length of 53 bytes, we skip over parsing the
|
||||
// field and do not return an error.
|
||||
func TestParseDestination(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testPubKeyData, _ := bech32.ConvertBits(testPubKey.SerializeCompressed(), 8, 5, true)
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result *btcec.PublicKey
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 53 bytes
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 53 bytes
|
||||
},
|
||||
{
|
||||
data: testPubKeyData,
|
||||
valid: true,
|
||||
result: testPubKey,
|
||||
},
|
||||
{
|
||||
data: append(testPubKeyData, 0x0),
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 53 bytes
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
destination, err := parseDestination(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("destination decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && !comparePubkeys(destination, test.result) {
|
||||
t.Fatalf("test %d failed decoding destination: "+
|
||||
"expected %x, got %x",
|
||||
i, *test.result, *destination)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseDescriptionHash checks that the description hash is properly parsed.
|
||||
// If the data does not have a length of 52 bytes, we skip over parsing the
|
||||
// field and do not return an error.
|
||||
func TestParseDescriptionHash(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDescriptionHashData, _ := bech32.ConvertBits(testDescriptionHash[:], 8, 5, true)
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result *[32]byte
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 52 bytes
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 52 bytes
|
||||
},
|
||||
{
|
||||
data: testDescriptionHashData,
|
||||
valid: true,
|
||||
result: &testDescriptionHash,
|
||||
},
|
||||
{
|
||||
data: append(testDescriptionHashData, 0x0),
|
||||
valid: true,
|
||||
result: nil, // skip unknown length, not 52 bytes
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
descriptionHash, err := parseDescriptionHash(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("description hash decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && !compareHashes(descriptionHash, test.result) {
|
||||
t.Fatalf("test %d failed decoding description hash: "+
|
||||
"expected %x, got %x",
|
||||
i, *test.result, *descriptionHash)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseExpiry checks that the expiry is properly parsed.
|
||||
func TestParseExpiry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result *time.Duration
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: &testExpiry0,
|
||||
},
|
||||
{
|
||||
data: []byte{0x1, 0x1c},
|
||||
valid: true,
|
||||
result: &testExpiry60,
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc},
|
||||
valid: false, // data too long
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
expiry, err := parseExpiry(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("expiry decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && !reflect.DeepEqual(expiry, test.result) {
|
||||
t.Fatalf("test %d failed decoding expiry: "+
|
||||
"expected expiry %v, got %v",
|
||||
i, *test.result, *expiry)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMinFinalCLTVExpiry checks that the minFinalCLTVExpiry is properly parsed.
|
||||
func TestParseMinFinalCLTVExpiry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result uint64
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: 0,
|
||||
},
|
||||
{
|
||||
data: []byte{0x1, 0x1c},
|
||||
valid: true,
|
||||
result: 60,
|
||||
},
|
||||
{
|
||||
data: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc},
|
||||
valid: true,
|
||||
result: 38390726480144748,
|
||||
},
|
||||
{
|
||||
data: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc},
|
||||
valid: false, // data too long
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
expiry, err := parseMinFinalCLTVExpiry(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("minFinalCLTVExpiry decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && *expiry != test.result {
|
||||
t.Fatalf("test %d failed decoding minFinalCLTVExpiry: "+
|
||||
"expected %d, got %d",
|
||||
i, test.result, *expiry)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseFallbackAddr checks that the fallback address is properly parsed.
|
||||
func TestParseFallbackAddr(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testAddrTestnetData, _ := bech32.ConvertBits(testAddrTestnet.ScriptAddress(), 8, 5, true)
|
||||
testAddrTestnetDataWithVersion := append([]byte{17}, testAddrTestnetData...)
|
||||
|
||||
testRustyAddrData, _ := bech32.ConvertBits(testRustyAddr.ScriptAddress(), 8, 5, true)
|
||||
testRustyAddrDataWithVersion := append([]byte{17}, testRustyAddrData...)
|
||||
|
||||
testAddrMainnetP2SHData, _ := bech32.ConvertBits(testAddrMainnetP2SH.ScriptAddress(), 8, 5, true)
|
||||
testAddrMainnetP2SHDataWithVersion := append([]byte{18}, testAddrMainnetP2SHData...)
|
||||
|
||||
testAddrMainnetP2WPKHData, _ := bech32.ConvertBits(testAddrMainnetP2WPKH.ScriptAddress(), 8, 5, true)
|
||||
testAddrMainnetP2WPKHDataWithVersion := append([]byte{0}, testAddrMainnetP2WPKHData...)
|
||||
|
||||
testAddrMainnetP2WSHData, _ := bech32.ConvertBits(testAddrMainnetP2WSH.ScriptAddress(), 8, 5, true)
|
||||
testAddrMainnetP2WSHDataWithVersion := append([]byte{0}, testAddrMainnetP2WSHData...)
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
net *chaincfg.Params
|
||||
valid bool
|
||||
result btcutil.Address
|
||||
}{
|
||||
{
|
||||
data: []byte{},
|
||||
valid: false, // empty data
|
||||
},
|
||||
{
|
||||
data: []byte{0x0},
|
||||
valid: false, // data too short, version without address
|
||||
},
|
||||
{
|
||||
data: testAddrTestnetDataWithVersion,
|
||||
net: &chaincfg.TestNet3Params,
|
||||
valid: true,
|
||||
result: testAddrTestnet,
|
||||
},
|
||||
{
|
||||
data: testRustyAddrDataWithVersion,
|
||||
net: &chaincfg.MainNetParams,
|
||||
valid: true,
|
||||
result: testRustyAddr,
|
||||
},
|
||||
{
|
||||
data: testAddrMainnetP2SHDataWithVersion,
|
||||
net: &chaincfg.MainNetParams,
|
||||
valid: true,
|
||||
result: testAddrMainnetP2SH,
|
||||
},
|
||||
{
|
||||
data: testAddrMainnetP2WPKHDataWithVersion,
|
||||
net: &chaincfg.MainNetParams,
|
||||
valid: true,
|
||||
result: testAddrMainnetP2WPKH,
|
||||
},
|
||||
{
|
||||
data: testAddrMainnetP2WSHDataWithVersion,
|
||||
net: &chaincfg.MainNetParams,
|
||||
valid: true,
|
||||
result: testAddrMainnetP2WSH,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
fallbackAddr, err := parseFallbackAddr(test.data, test.net)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("fallback addr decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid && !reflect.DeepEqual(test.result, fallbackAddr) {
|
||||
t.Fatalf("test %d failed decoding fallback addr: "+
|
||||
"expected %v, got %v",
|
||||
i, test.result, fallbackAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseRoutingInfo checks that the routing info is properly parsed.
|
||||
func TestParseRoutingInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var testSingleHopData []byte
|
||||
for _, r := range testSingleHop {
|
||||
base256 := make([]byte, 51)
|
||||
copy(base256[:33], r.PubKey.SerializeCompressed())
|
||||
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
||||
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
||||
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
||||
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
||||
testSingleHopData = append(testSingleHopData, base256...)
|
||||
}
|
||||
testSingleHopData, _ = bech32.ConvertBits(testSingleHopData, 8, 5, true)
|
||||
|
||||
var testDoubleHopData []byte
|
||||
for _, r := range testDoubleHop {
|
||||
base256 := make([]byte, 51)
|
||||
copy(base256[:33], r.PubKey.SerializeCompressed())
|
||||
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
||||
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
||||
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
||||
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
||||
testDoubleHopData = append(testDoubleHopData, base256...)
|
||||
}
|
||||
testDoubleHopData, _ = bech32.ConvertBits(testDoubleHopData, 8, 5, true)
|
||||
|
||||
tests := []struct {
|
||||
data []byte
|
||||
valid bool
|
||||
result []ExtraRoutingInfo
|
||||
}{
|
||||
{
|
||||
data: []byte{0x0, 0x0, 0x0, 0x0},
|
||||
valid: false, // data too short, not multiple of 51 bytes
|
||||
},
|
||||
{
|
||||
data: []byte{},
|
||||
valid: true,
|
||||
result: []ExtraRoutingInfo{},
|
||||
},
|
||||
{
|
||||
data: testSingleHopData,
|
||||
valid: true,
|
||||
result: testSingleHop,
|
||||
},
|
||||
{
|
||||
data: append(testSingleHopData, 0x0),
|
||||
valid: false, // data too long, not multiple of 51 bytes
|
||||
},
|
||||
{
|
||||
data: testDoubleHopData,
|
||||
valid: true,
|
||||
result: testDoubleHop,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
routingInfo, err := parseRoutingInfo(test.data)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("routing info decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
if test.valid {
|
||||
if err := compareRoutingInfos(test.result, routingInfo); err != nil {
|
||||
t.Fatalf("test %d failed decoding routing info: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user