channeldb/test: unit tests for payments query

Adds tests for the payments query, where different edge cases for
the index offsets and normal and reversed orders are tested.
This commit is contained in:
bitromortac 2020-01-17 07:31:07 +01:00
parent 4c5e8aec41
commit d5dd48fa71

@ -3,6 +3,7 @@ package channeldb
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math"
"math/rand" "math/rand"
"reflect" "reflect"
"testing" "testing"
@ -10,6 +11,7 @@ import (
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb/kvdb"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
@ -175,3 +177,264 @@ func TestRouteSerialization(t *testing.T) {
spew.Sdump(testRoute), spew.Sdump(route2)) spew.Sdump(testRoute), spew.Sdump(route2))
} }
} }
// deletePayment removes a payment with paymentHash from the payments database.
func deletePayment(t *testing.T, db *DB, paymentHash lntypes.Hash) {
t.Helper()
err := kvdb.Update(db, func(tx kvdb.RwTx) error {
payments := tx.ReadWriteBucket(paymentsRootBucket)
err := payments.DeleteNestedBucket(paymentHash[:])
if err != nil {
return err
}
return nil
})
if err != nil {
t.Fatalf("could not delete "+
"payment: %v", err)
}
}
// TestQueryPayments tests retrieval of payments with forwards and reversed
// queries.
func TestQueryPayments(t *testing.T) {
// Define table driven test for QueryPayments.
// Test payments have sequence indices [1, 3, 4, 5, 6, 7].
tests := []struct {
name string
query PaymentsQuery
firstIndex uint64
lastIndex uint64
// expectedSeqNrs contains the set of sequence numbers we expect
// our query to return.
expectedSeqNrs []uint64
}{
{
name: "IndexOffset at the end of the payments range",
query: PaymentsQuery{
IndexOffset: 7,
MaxPayments: 7,
Reversed: false,
IncludeIncomplete: true,
},
firstIndex: 0,
lastIndex: 0,
expectedSeqNrs: nil,
},
{
name: "query in forwards order, start at beginning",
query: PaymentsQuery{
IndexOffset: 0,
MaxPayments: 2,
Reversed: false,
IncludeIncomplete: true,
},
firstIndex: 1,
lastIndex: 3,
expectedSeqNrs: []uint64{1, 3},
},
{
name: "query in forwards order, start at end, overflow",
query: PaymentsQuery{
IndexOffset: 6,
MaxPayments: 2,
Reversed: false,
IncludeIncomplete: true,
},
firstIndex: 7,
lastIndex: 7,
expectedSeqNrs: []uint64{7},
},
{
name: "start at offset index outside of payments",
query: PaymentsQuery{
IndexOffset: 20,
MaxPayments: 2,
Reversed: false,
IncludeIncomplete: true,
},
firstIndex: 0,
lastIndex: 0,
expectedSeqNrs: nil,
},
{
name: "overflow in forwards order",
query: PaymentsQuery{
IndexOffset: 4,
MaxPayments: math.MaxUint64,
Reversed: false,
IncludeIncomplete: true,
},
firstIndex: 5,
lastIndex: 7,
expectedSeqNrs: []uint64{5, 6, 7},
},
{
name: "start at offset index outside of payments, " +
"reversed order",
query: PaymentsQuery{
IndexOffset: 9,
MaxPayments: 2,
Reversed: true,
IncludeIncomplete: true,
},
firstIndex: 6,
lastIndex: 7,
expectedSeqNrs: []uint64{6, 7},
},
{
name: "query in reverse order, start at end",
query: PaymentsQuery{
IndexOffset: 0,
MaxPayments: 2,
Reversed: true,
IncludeIncomplete: true,
},
firstIndex: 6,
lastIndex: 7,
expectedSeqNrs: []uint64{6, 7},
},
{
name: "query in reverse order, starting in middle",
query: PaymentsQuery{
IndexOffset: 4,
MaxPayments: 2,
Reversed: true,
IncludeIncomplete: true,
},
firstIndex: 1,
lastIndex: 3,
expectedSeqNrs: []uint64{1, 3},
},
{
name: "query in reverse order, starting in middle, " +
"with underflow",
query: PaymentsQuery{
IndexOffset: 4,
MaxPayments: 5,
Reversed: true,
IncludeIncomplete: true,
},
firstIndex: 1,
lastIndex: 3,
expectedSeqNrs: []uint64{1, 3},
},
{
name: "all payments in reverse, order maintained",
query: PaymentsQuery{
IndexOffset: 0,
MaxPayments: 7,
Reversed: true,
IncludeIncomplete: true,
},
firstIndex: 1,
lastIndex: 7,
expectedSeqNrs: []uint64{1, 3, 4, 5, 6, 7},
},
{
name: "exclude incomplete payments",
query: PaymentsQuery{
IndexOffset: 0,
MaxPayments: 7,
Reversed: false,
IncludeIncomplete: false,
},
firstIndex: 0,
lastIndex: 0,
expectedSeqNrs: nil,
},
{
name: "query payments at index gap",
query: PaymentsQuery{
IndexOffset: 1,
MaxPayments: 7,
Reversed: false,
IncludeIncomplete: true,
},
firstIndex: 3,
lastIndex: 7,
expectedSeqNrs: []uint64{3, 4, 5, 6, 7},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
db, err := initDB()
if err != nil {
t.Fatalf("unable to init db: %v", err)
}
// Populate the database with a set of test payments.
numberOfPayments := 7
pControl := NewPaymentControl(db)
for i := 0; i < numberOfPayments; i++ {
// Generate a test payment.
info, _, _, err := genInfo()
if err != nil {
t.Fatalf("unable to create test "+
"payment: %v", err)
}
// Create a new payment entry in the database.
err = pControl.InitPayment(info.PaymentHash, info)
if err != nil {
t.Fatalf("unable to initialize "+
"payment in database: %v", err)
}
// Immediately delete the payment with index 2.
if i == 1 {
deletePayment(t, db, info.PaymentHash)
}
}
// Fetch all payments in the database.
allPayments, err := db.FetchPayments()
if err != nil {
t.Fatalf("payments could not be fetched from "+
"database: %v", err)
}
if len(allPayments) != 6 {
t.Fatalf("Number of payments received does not "+
"match expected one. Got %v, want %v.",
len(allPayments), 6)
}
querySlice, err := db.QueryPayments(tt.query)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tt.firstIndex != querySlice.FirstIndexOffset ||
tt.lastIndex != querySlice.LastIndexOffset {
t.Errorf("First or last index does not match "+
"expected index. Want (%d, %d), got (%d, %d).",
tt.firstIndex, tt.lastIndex,
querySlice.FirstIndexOffset,
querySlice.LastIndexOffset)
}
if len(querySlice.Payments) != len(tt.expectedSeqNrs) {
t.Errorf("expected: %v payments, got: %v",
len(allPayments), len(querySlice.Payments))
}
for i, seqNr := range tt.expectedSeqNrs {
q := querySlice.Payments[i]
if seqNr != q.SequenceNum {
t.Errorf("sequence numbers do not match, "+
"got %v, want %v", q.SequenceNum, seqNr)
}
}
})
}
}