routing/test: run each test case individually, add names

Update our payment lifecycle test to run each test case with
a fresh router. This prevents test cases from interacting with
each other. Names are also added for easy debugging.
This commit is contained in:
carla 2021-04-23 08:39:37 +02:00
parent cb927e89b0
commit 806c4cbd57
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91

@ -48,65 +48,20 @@ func createTestRoute(amt lnwire.MilliSatoshi,
)
}
// TestRouterPaymentStateMachine tests that the router interacts as expected
// with the ControlTower during a payment lifecycle, such that it payment
// attempts are not sent twice to the switch, and results are handled after a
// restart.
func TestRouterPaymentStateMachine(t *testing.T) {
t.Parallel()
// paymentLifecycleTestCase contains the steps that we expect for a payment
// lifecycle test, and the routes that pathfinding should deliver.
type paymentLifecycleTestCase struct {
name string
const startingBlockHeight = 101
// Setup two simple channels such that we can mock sending along this
// route.
chanCapSat := btcutil.Amount(100000)
testChannels := []*testChannel{
symmetricTestChannel("a", "b", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
}, 1),
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
}, 2),
}
testGraph, err := createTestGraphFromChannels(testChannels, "a")
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraph.cleanUp()
paymentAmt := lnwire.NewMSatFromSatoshis(1000)
// We create a simple route that we will supply every time the router
// requests one.
rt, err := createTestRoute(paymentAmt, testGraph.aliasMap)
if err != nil {
t.Fatalf("unable to create route: %v", err)
}
shard, err := createTestRoute(paymentAmt/4, testGraph.aliasMap)
if err != nil {
t.Fatalf("unable to create route: %v", err)
}
// A payment state machine test case consists of several ordered steps,
// that we use for driving the scenario.
type testCase struct {
// steps is a list of steps to perform during the testcase.
steps []string
// routes is the sequence of routes we will provide to the
// router when it requests a new route.
routes []*route.Route
}
}
const (
const (
// routerInitPayment is a test step where we expect the router
// to call the InitPayment method on the control tower.
routerInitPayment = "Router:init-payment"
@ -186,11 +141,60 @@ func TestRouterPaymentStateMachine(t *testing.T) {
// resentPaymentError is a step where assert that we receive an
// error for a payment that was resent.
resentPaymentError = "ResentPaymentError"
)
)
tests := []testCase{
// TestRouterPaymentStateMachine tests that the router interacts as expected
// with the ControlTower during a payment lifecycle, such that it payment
// attempts are not sent twice to the switch, and results are handled after a
// restart.
func TestRouterPaymentStateMachine(t *testing.T) {
t.Parallel()
const startingBlockHeight = 101
// Setup two simple channels such that we can mock sending along this
// route.
chanCapSat := btcutil.Amount(100000)
testChannels := []*testChannel{
symmetricTestChannel("a", "b", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
}, 1),
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
Expiry: 144,
FeeRate: 400,
MinHTLC: 1,
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
}, 2),
}
testGraph, err := createTestGraphFromChannels(testChannels, "a")
if err != nil {
t.Fatalf("unable to create graph: %v", err)
}
defer testGraph.cleanUp()
paymentAmt := lnwire.NewMSatFromSatoshis(1000)
// We create a simple route that we will supply every time the router
// requests one.
rt, err := createTestRoute(paymentAmt, testGraph.aliasMap)
if err != nil {
t.Fatalf("unable to create route: %v", err)
}
shard, err := createTestRoute(paymentAmt/4, testGraph.aliasMap)
if err != nil {
t.Fatalf("unable to create route: %v", err)
}
tests := []paymentLifecycleTestCase{
{
// Tests a normal payment flow that succeeds.
name: "single shot success",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -204,6 +208,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
{
// A payment flow with a failure on the first attempt,
// but that succeeds on the second attempt.
name: "single shot retry",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -228,6 +234,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
// A payment flow with a forwarding failure first time
// sending to the switch, but that succeeds on the
// second attempt.
name: "single shot switch failure",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -251,6 +259,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
// A payment that fails on the first attempt, and has
// only one route available to try. It will therefore
// fail permanently.
name: "single shot route fails",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -270,6 +280,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
{
// We expect the payment to fail immediately if we have
// no routes to try.
name: "single shot no route",
steps: []string{
routerInitPayment,
routerFailPayment,
@ -282,6 +294,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
// the same payment after each step. This ensures that
// the router don't attempt to resend a payment already
// in flight.
name: "single shot resend",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -322,6 +336,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
{
// Tests that the router is able to handle the
// receieved payment result after a restart.
name: "single shot restart",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -344,6 +360,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
{
// Tests that we are allowed to resend a payment after
// it has permanently failed.
name: "single shot resend fail",
steps: []string{
routerInitPayment,
routerRegisterAttempt,
@ -382,6 +400,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
// =====================================
{
// Tests a simple successful MP payment of 4 shards.
name: "MP success",
steps: []string{
routerInitPayment,
@ -422,6 +442,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
{
// An MP payment scenario where we need several extra
// attempts before the payment finally settle.
name: "MP failed shards",
steps: []string{
routerInitPayment,
@ -474,6 +496,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
// However the last shard settle, which means we get
// the preimage and should consider the overall payment
// a success.
name: "MP one shard success",
steps: []string{
routerInitPayment,
@ -513,6 +537,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
{
// An MP payment scenario a shard fail with a terminal
// error, causing the router to stop attempting.
name: "MP terminal",
steps: []string{
routerInitPayment,
@ -554,6 +580,21 @@ func TestRouterPaymentStateMachine(t *testing.T) {
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
testPaymentLifecycle(
t, test, paymentAmt, startingBlockHeight,
testGraph,
)
})
}
}
func testPaymentLifecycle(t *testing.T, test paymentLifecycleTestCase,
paymentAmt lnwire.MilliSatoshi, startingBlockHeight uint32,
testGraph *testGraphInstance) {
// Create a mock control tower with channels set up, that we use to
// synchronize and listen for events.
control := makeMockControlTower()
@ -644,7 +685,6 @@ func TestRouterPaymentStateMachine(t *testing.T) {
}
}()
for _, test := range tests {
// Craft a LightningPayment struct.
var preImage lntypes.Preimage
if _, err := rand.Read(preImage[:]); err != nil {
@ -899,5 +939,4 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case <-time.After(testTimeout):
t.Fatalf("SendPayment didn't exit")
}
}
}