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:
parent
cb927e89b0
commit
806c4cbd57
@ -48,6 +48,101 @@ func createTestRoute(amt lnwire.MilliSatoshi,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paymentLifecycleTestCase contains the steps that we expect for a payment
|
||||||
|
// lifecycle test, and the routes that pathfinding should deliver.
|
||||||
|
type paymentLifecycleTestCase struct {
|
||||||
|
name string
|
||||||
|
|
||||||
|
// 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 (
|
||||||
|
// routerInitPayment is a test step where we expect the router
|
||||||
|
// to call the InitPayment method on the control tower.
|
||||||
|
routerInitPayment = "Router:init-payment"
|
||||||
|
|
||||||
|
// routerRegisterAttempt is a test step where we expect the
|
||||||
|
// router to call the RegisterAttempt method on the control
|
||||||
|
// tower.
|
||||||
|
routerRegisterAttempt = "Router:register-attempt"
|
||||||
|
|
||||||
|
// routerSettleAttempt is a test step where we expect the
|
||||||
|
// router to call the SettleAttempt method on the control
|
||||||
|
// tower.
|
||||||
|
routerSettleAttempt = "Router:settle-attempt"
|
||||||
|
|
||||||
|
// routerFailAttempt is a test step where we expect the router
|
||||||
|
// to call the FailAttempt method on the control tower.
|
||||||
|
routerFailAttempt = "Router:fail-attempt"
|
||||||
|
|
||||||
|
// routerFailPayment is a test step where we expect the router
|
||||||
|
// to call the Fail method on the control tower.
|
||||||
|
routerFailPayment = "Router:fail-payment"
|
||||||
|
|
||||||
|
// sendToSwitchSuccess is a step where we expect the router to
|
||||||
|
// call send the payment attempt to the switch, and we will
|
||||||
|
// respond with a non-error, indicating that the payment
|
||||||
|
// attempt was successfully forwarded.
|
||||||
|
sendToSwitchSuccess = "SendToSwitch:success"
|
||||||
|
|
||||||
|
// sendToSwitchResultFailure is a step where we expect the
|
||||||
|
// router to send the payment attempt to the switch, and we
|
||||||
|
// will respond with a forwarding error. This can happen when
|
||||||
|
// forwarding fail on our local links.
|
||||||
|
sendToSwitchResultFailure = "SendToSwitch:failure"
|
||||||
|
|
||||||
|
// getPaymentResultSuccess is a test step where we expect the
|
||||||
|
// router to call the GetPaymentResult method, and we will
|
||||||
|
// respond with a successful payment result.
|
||||||
|
getPaymentResultSuccess = "GetPaymentResult:success"
|
||||||
|
|
||||||
|
// getPaymentResultTempFailure is a test step where we expect the
|
||||||
|
// router to call the GetPaymentResult method, and we will
|
||||||
|
// respond with a forwarding error, expecting the router to retry.
|
||||||
|
getPaymentResultTempFailure = "GetPaymentResult:temp-failure"
|
||||||
|
|
||||||
|
// getPaymentResultTerminalFailure is a test step where we
|
||||||
|
// expect the router to call the GetPaymentResult method, and
|
||||||
|
// we will respond with a terminal error, expecting the router
|
||||||
|
// to stop making payment attempts.
|
||||||
|
getPaymentResultTerminalFailure = "GetPaymentResult:terminal-failure"
|
||||||
|
|
||||||
|
// resendPayment is a test step where we manually try to resend
|
||||||
|
// the same payment, making sure the router responds with an
|
||||||
|
// error indicating that it is already in flight.
|
||||||
|
resendPayment = "ResendPayment"
|
||||||
|
|
||||||
|
// startRouter is a step where we manually start the router,
|
||||||
|
// used to test that it automatically will resume payments at
|
||||||
|
// startup.
|
||||||
|
startRouter = "StartRouter"
|
||||||
|
|
||||||
|
// stopRouter is a test step where we manually make the router
|
||||||
|
// shut down.
|
||||||
|
stopRouter = "StopRouter"
|
||||||
|
|
||||||
|
// paymentSuccess is a step where assert that we receive a
|
||||||
|
// successful result for the original payment made.
|
||||||
|
paymentSuccess = "PaymentSuccess"
|
||||||
|
|
||||||
|
// paymentError is a step where assert that we receive an error
|
||||||
|
// for the original payment made.
|
||||||
|
paymentError = "PaymentError"
|
||||||
|
|
||||||
|
// resentPaymentSuccess is a step where assert that we receive
|
||||||
|
// a successful result for a payment that was resent.
|
||||||
|
resentPaymentSuccess = "ResentPaymentSuccess"
|
||||||
|
|
||||||
|
// resentPaymentError is a step where assert that we receive an
|
||||||
|
// error for a payment that was resent.
|
||||||
|
resentPaymentError = "ResentPaymentError"
|
||||||
|
)
|
||||||
|
|
||||||
// TestRouterPaymentStateMachine tests that the router interacts as expected
|
// TestRouterPaymentStateMachine tests that the router interacts as expected
|
||||||
// with the ControlTower during a payment lifecycle, such that it payment
|
// 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
|
// attempts are not sent twice to the switch, and results are handled after a
|
||||||
@ -95,102 +190,11 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
t.Fatalf("unable to create route: %v", err)
|
t.Fatalf("unable to create route: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A payment state machine test case consists of several ordered steps,
|
tests := []paymentLifecycleTestCase{
|
||||||
// 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 (
|
|
||||||
// routerInitPayment is a test step where we expect the router
|
|
||||||
// to call the InitPayment method on the control tower.
|
|
||||||
routerInitPayment = "Router:init-payment"
|
|
||||||
|
|
||||||
// routerRegisterAttempt is a test step where we expect the
|
|
||||||
// router to call the RegisterAttempt method on the control
|
|
||||||
// tower.
|
|
||||||
routerRegisterAttempt = "Router:register-attempt"
|
|
||||||
|
|
||||||
// routerSettleAttempt is a test step where we expect the
|
|
||||||
// router to call the SettleAttempt method on the control
|
|
||||||
// tower.
|
|
||||||
routerSettleAttempt = "Router:settle-attempt"
|
|
||||||
|
|
||||||
// routerFailAttempt is a test step where we expect the router
|
|
||||||
// to call the FailAttempt method on the control tower.
|
|
||||||
routerFailAttempt = "Router:fail-attempt"
|
|
||||||
|
|
||||||
// routerFailPayment is a test step where we expect the router
|
|
||||||
// to call the Fail method on the control tower.
|
|
||||||
routerFailPayment = "Router:fail-payment"
|
|
||||||
|
|
||||||
// sendToSwitchSuccess is a step where we expect the router to
|
|
||||||
// call send the payment attempt to the switch, and we will
|
|
||||||
// respond with a non-error, indicating that the payment
|
|
||||||
// attempt was successfully forwarded.
|
|
||||||
sendToSwitchSuccess = "SendToSwitch:success"
|
|
||||||
|
|
||||||
// sendToSwitchResultFailure is a step where we expect the
|
|
||||||
// router to send the payment attempt to the switch, and we
|
|
||||||
// will respond with a forwarding error. This can happen when
|
|
||||||
// forwarding fail on our local links.
|
|
||||||
sendToSwitchResultFailure = "SendToSwitch:failure"
|
|
||||||
|
|
||||||
// getPaymentResultSuccess is a test step where we expect the
|
|
||||||
// router to call the GetPaymentResult method, and we will
|
|
||||||
// respond with a successful payment result.
|
|
||||||
getPaymentResultSuccess = "GetPaymentResult:success"
|
|
||||||
|
|
||||||
// getPaymentResultTempFailure is a test step where we expect the
|
|
||||||
// router to call the GetPaymentResult method, and we will
|
|
||||||
// respond with a forwarding error, expecting the router to retry.
|
|
||||||
getPaymentResultTempFailure = "GetPaymentResult:temp-failure"
|
|
||||||
|
|
||||||
// getPaymentResultTerminalFailure is a test step where we
|
|
||||||
// expect the router to call the GetPaymentResult method, and
|
|
||||||
// we will respond with a terminal error, expecting the router
|
|
||||||
// to stop making payment attempts.
|
|
||||||
getPaymentResultTerminalFailure = "GetPaymentResult:terminal-failure"
|
|
||||||
|
|
||||||
// resendPayment is a test step where we manually try to resend
|
|
||||||
// the same payment, making sure the router responds with an
|
|
||||||
// error indicating that it is already in flight.
|
|
||||||
resendPayment = "ResendPayment"
|
|
||||||
|
|
||||||
// startRouter is a step where we manually start the router,
|
|
||||||
// used to test that it automatically will resume payments at
|
|
||||||
// startup.
|
|
||||||
startRouter = "StartRouter"
|
|
||||||
|
|
||||||
// stopRouter is a test step where we manually make the router
|
|
||||||
// shut down.
|
|
||||||
stopRouter = "StopRouter"
|
|
||||||
|
|
||||||
// paymentSuccess is a step where assert that we receive a
|
|
||||||
// successful result for the original payment made.
|
|
||||||
paymentSuccess = "PaymentSuccess"
|
|
||||||
|
|
||||||
// paymentError is a step where assert that we receive an error
|
|
||||||
// for the original payment made.
|
|
||||||
paymentError = "PaymentError"
|
|
||||||
|
|
||||||
// resentPaymentSuccess is a step where assert that we receive
|
|
||||||
// a successful result for a payment that was resent.
|
|
||||||
resentPaymentSuccess = "ResentPaymentSuccess"
|
|
||||||
|
|
||||||
// resentPaymentError is a step where assert that we receive an
|
|
||||||
// error for a payment that was resent.
|
|
||||||
resentPaymentError = "ResentPaymentError"
|
|
||||||
)
|
|
||||||
|
|
||||||
tests := []testCase{
|
|
||||||
{
|
{
|
||||||
// Tests a normal payment flow that succeeds.
|
// Tests a normal payment flow that succeeds.
|
||||||
|
name: "single shot success",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -204,6 +208,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// A payment flow with a failure on the first attempt,
|
// A payment flow with a failure on the first attempt,
|
||||||
// but that succeeds on the second attempt.
|
// but that succeeds on the second attempt.
|
||||||
|
name: "single shot retry",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -228,6 +234,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
// A payment flow with a forwarding failure first time
|
// A payment flow with a forwarding failure first time
|
||||||
// sending to the switch, but that succeeds on the
|
// sending to the switch, but that succeeds on the
|
||||||
// second attempt.
|
// second attempt.
|
||||||
|
name: "single shot switch failure",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -251,6 +259,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
// A payment that fails on the first attempt, and has
|
// A payment that fails on the first attempt, and has
|
||||||
// only one route available to try. It will therefore
|
// only one route available to try. It will therefore
|
||||||
// fail permanently.
|
// fail permanently.
|
||||||
|
name: "single shot route fails",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -270,6 +280,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// We expect the payment to fail immediately if we have
|
// We expect the payment to fail immediately if we have
|
||||||
// no routes to try.
|
// no routes to try.
|
||||||
|
name: "single shot no route",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerFailPayment,
|
routerFailPayment,
|
||||||
@ -282,6 +294,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
// the same payment after each step. This ensures that
|
// the same payment after each step. This ensures that
|
||||||
// the router don't attempt to resend a payment already
|
// the router don't attempt to resend a payment already
|
||||||
// in flight.
|
// in flight.
|
||||||
|
name: "single shot resend",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -322,6 +336,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// Tests that the router is able to handle the
|
// Tests that the router is able to handle the
|
||||||
// receieved payment result after a restart.
|
// receieved payment result after a restart.
|
||||||
|
name: "single shot restart",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -344,6 +360,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// Tests that we are allowed to resend a payment after
|
// Tests that we are allowed to resend a payment after
|
||||||
// it has permanently failed.
|
// it has permanently failed.
|
||||||
|
name: "single shot resend fail",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
routerRegisterAttempt,
|
routerRegisterAttempt,
|
||||||
@ -382,6 +400,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
// =====================================
|
// =====================================
|
||||||
{
|
{
|
||||||
// Tests a simple successful MP payment of 4 shards.
|
// Tests a simple successful MP payment of 4 shards.
|
||||||
|
name: "MP success",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
|
|
||||||
@ -422,6 +442,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// An MP payment scenario where we need several extra
|
// An MP payment scenario where we need several extra
|
||||||
// attempts before the payment finally settle.
|
// attempts before the payment finally settle.
|
||||||
|
name: "MP failed shards",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
|
|
||||||
@ -474,6 +496,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
// However the last shard settle, which means we get
|
// However the last shard settle, which means we get
|
||||||
// the preimage and should consider the overall payment
|
// the preimage and should consider the overall payment
|
||||||
// a success.
|
// a success.
|
||||||
|
name: "MP one shard success",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
routerInitPayment,
|
||||||
|
|
||||||
@ -513,6 +537,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// An MP payment scenario a shard fail with a terminal
|
// An MP payment scenario a shard fail with a terminal
|
||||||
// error, causing the router to stop attempting.
|
// error, causing the router to stop attempting.
|
||||||
|
name: "MP terminal",
|
||||||
|
|
||||||
steps: []string{
|
steps: []string{
|
||||||
routerInitPayment,
|
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
|
// Create a mock control tower with channels set up, that we use to
|
||||||
// synchronize and listen for events.
|
// synchronize and listen for events.
|
||||||
control := makeMockControlTower()
|
control := makeMockControlTower()
|
||||||
@ -644,260 +685,258 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, test := range tests {
|
// Craft a LightningPayment struct.
|
||||||
// Craft a LightningPayment struct.
|
var preImage lntypes.Preimage
|
||||||
var preImage lntypes.Preimage
|
if _, err := rand.Read(preImage[:]); err != nil {
|
||||||
if _, err := rand.Read(preImage[:]); err != nil {
|
t.Fatalf("unable to generate preimage")
|
||||||
t.Fatalf("unable to generate preimage")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
payHash := preImage.Hash()
|
payHash := preImage.Hash()
|
||||||
|
|
||||||
payment := LightningPayment{
|
payment := LightningPayment{
|
||||||
Target: testGraph.aliasMap["c"],
|
Target: testGraph.aliasMap["c"],
|
||||||
Amount: paymentAmt,
|
Amount: paymentAmt,
|
||||||
FeeLimit: noFeeLimit,
|
FeeLimit: noFeeLimit,
|
||||||
PaymentHash: payHash,
|
PaymentHash: payHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.cfg.SessionSource = &mockPaymentSessionSource{
|
router.cfg.SessionSource = &mockPaymentSessionSource{
|
||||||
routes: test.routes,
|
routes: test.routes,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.cfg.MissionControl = &mockMissionControl{}
|
router.cfg.MissionControl = &mockMissionControl{}
|
||||||
|
|
||||||
// Send the payment. Since this is new payment hash, the
|
// Send the payment. Since this is new payment hash, the
|
||||||
// information should be registered with the ControlTower.
|
// information should be registered with the ControlTower.
|
||||||
paymentResult := make(chan error)
|
paymentResult := make(chan error)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
_, _, err := router.SendPayment(&payment)
|
_, _, err := router.SendPayment(&payment)
|
||||||
paymentResult <- err
|
paymentResult <- err
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var resendResult chan error
|
var resendResult chan error
|
||||||
for _, step := range test.steps {
|
for _, step := range test.steps {
|
||||||
switch step {
|
switch step {
|
||||||
|
|
||||||
case routerInitPayment:
|
case routerInitPayment:
|
||||||
var args initArgs
|
var args initArgs
|
||||||
select {
|
select {
|
||||||
case args = <-control.init:
|
case args = <-control.init:
|
||||||
case <-time.After(stepTimeout):
|
case <-time.After(stepTimeout):
|
||||||
t.Fatalf("no init payment with control")
|
t.Fatalf("no init payment with control")
|
||||||
}
|
|
||||||
|
|
||||||
if args.c == nil {
|
|
||||||
t.Fatalf("expected non-nil CreationInfo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the router to make a call to
|
|
||||||
// register a new attempt with the ControlTower.
|
|
||||||
case routerRegisterAttempt:
|
|
||||||
var args registerAttemptArgs
|
|
||||||
select {
|
|
||||||
case args = <-control.registerAttempt:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("attempt not registered " +
|
|
||||||
"with control")
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.a == nil {
|
|
||||||
t.Fatalf("expected non-nil AttemptInfo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the router to call the
|
|
||||||
// ControlTower's SettleAttempt method with the preimage.
|
|
||||||
case routerSettleAttempt:
|
|
||||||
select {
|
|
||||||
case <-control.settleAttempt:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("attempt settle not " +
|
|
||||||
"registered with control")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the router to call the
|
|
||||||
// ControlTower's FailAttempt method with a HTLC fail
|
|
||||||
// info.
|
|
||||||
case routerFailAttempt:
|
|
||||||
select {
|
|
||||||
case <-control.failAttempt:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("attempt fail not " +
|
|
||||||
"registered with control")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the router to call the
|
|
||||||
// ControlTower's Fail method, to indicate that the
|
|
||||||
// payment failed.
|
|
||||||
case routerFailPayment:
|
|
||||||
select {
|
|
||||||
case <-control.failPayment:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("payment fail not " +
|
|
||||||
"registered with control")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the SendToSwitch method to be
|
|
||||||
// called, and we respond with a nil-error.
|
|
||||||
case sendToSwitchSuccess:
|
|
||||||
select {
|
|
||||||
case sendResult <- nil:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("unable to send result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the SendToSwitch method to be
|
|
||||||
// called, and we respond with a forwarding error
|
|
||||||
case sendToSwitchResultFailure:
|
|
||||||
select {
|
|
||||||
case sendResult <- htlcswitch.NewForwardingError(
|
|
||||||
&lnwire.FailTemporaryChannelFailure{},
|
|
||||||
1,
|
|
||||||
):
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("unable to send result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we expect the GetPaymentResult method
|
|
||||||
// to be called, and we respond with the preimage to
|
|
||||||
// complete the payment.
|
|
||||||
case getPaymentResultSuccess:
|
|
||||||
select {
|
|
||||||
case getPaymentResult <- &htlcswitch.PaymentResult{
|
|
||||||
Preimage: preImage,
|
|
||||||
}:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("unable to send result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this state we expect the GetPaymentResult method
|
|
||||||
// to be called, and we respond with a forwarding
|
|
||||||
// error, indicating that the router should retry.
|
|
||||||
case getPaymentResultTempFailure:
|
|
||||||
failure := htlcswitch.NewForwardingError(
|
|
||||||
&lnwire.FailTemporaryChannelFailure{},
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case getPaymentResult <- &htlcswitch.PaymentResult{
|
|
||||||
Error: failure,
|
|
||||||
}:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("unable to get result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this state we expect the router to call the
|
|
||||||
// GetPaymentResult method, and we will respond with a
|
|
||||||
// terminal error, indiating the router should stop
|
|
||||||
// making payment attempts.
|
|
||||||
case getPaymentResultTerminalFailure:
|
|
||||||
failure := htlcswitch.NewForwardingError(
|
|
||||||
&lnwire.FailIncorrectDetails{},
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case getPaymentResult <- &htlcswitch.PaymentResult{
|
|
||||||
Error: failure,
|
|
||||||
}:
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("unable to get result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we manually try to resend the same
|
|
||||||
// payment, making sure the router responds with an
|
|
||||||
// error indicating that it is already in flight.
|
|
||||||
case resendPayment:
|
|
||||||
resendResult = make(chan error)
|
|
||||||
go func() {
|
|
||||||
_, _, err := router.SendPayment(&payment)
|
|
||||||
resendResult <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
// In this step we manually stop the router.
|
|
||||||
case stopRouter:
|
|
||||||
select {
|
|
||||||
case getPaymentResultErr <- fmt.Errorf(
|
|
||||||
"shutting down"):
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("unable to send payment " +
|
|
||||||
"result error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := router.Stop(); err != nil {
|
|
||||||
t.Fatalf("unable to restart: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this step we manually start the router.
|
|
||||||
case startRouter:
|
|
||||||
router, sendResult, getPaymentResult,
|
|
||||||
getPaymentResultErr = setupRouter()
|
|
||||||
|
|
||||||
// In this state we expect to receive an error for the
|
|
||||||
// original payment made.
|
|
||||||
case paymentError:
|
|
||||||
select {
|
|
||||||
case err := <-paymentResult:
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("got no payment result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this state we expect the original payment to
|
|
||||||
// succeed.
|
|
||||||
case paymentSuccess:
|
|
||||||
select {
|
|
||||||
case err := <-paymentResult:
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("did not expect "+
|
|
||||||
"error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("got no payment result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this state we expect to receive an error for the
|
|
||||||
// resent payment made.
|
|
||||||
case resentPaymentError:
|
|
||||||
select {
|
|
||||||
case err := <-resendResult:
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("got no payment result")
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this state we expect the resent payment to
|
|
||||||
// succeed.
|
|
||||||
case resentPaymentSuccess:
|
|
||||||
select {
|
|
||||||
case err := <-resendResult:
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("did not expect error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-time.After(stepTimeout):
|
|
||||||
t.Fatalf("got no payment result")
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
t.Fatalf("unknown step %v", step)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
if args.c == nil {
|
||||||
case <-done:
|
t.Fatalf("expected non-nil CreationInfo")
|
||||||
case <-time.After(testTimeout):
|
}
|
||||||
t.Fatalf("SendPayment didn't exit")
|
|
||||||
|
// In this step we expect the router to make a call to
|
||||||
|
// register a new attempt with the ControlTower.
|
||||||
|
case routerRegisterAttempt:
|
||||||
|
var args registerAttemptArgs
|
||||||
|
select {
|
||||||
|
case args = <-control.registerAttempt:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("attempt not registered " +
|
||||||
|
"with control")
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.a == nil {
|
||||||
|
t.Fatalf("expected non-nil AttemptInfo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we expect the router to call the
|
||||||
|
// ControlTower's SettleAttempt method with the preimage.
|
||||||
|
case routerSettleAttempt:
|
||||||
|
select {
|
||||||
|
case <-control.settleAttempt:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("attempt settle not " +
|
||||||
|
"registered with control")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we expect the router to call the
|
||||||
|
// ControlTower's FailAttempt method with a HTLC fail
|
||||||
|
// info.
|
||||||
|
case routerFailAttempt:
|
||||||
|
select {
|
||||||
|
case <-control.failAttempt:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("attempt fail not " +
|
||||||
|
"registered with control")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we expect the router to call the
|
||||||
|
// ControlTower's Fail method, to indicate that the
|
||||||
|
// payment failed.
|
||||||
|
case routerFailPayment:
|
||||||
|
select {
|
||||||
|
case <-control.failPayment:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("payment fail not " +
|
||||||
|
"registered with control")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we expect the SendToSwitch method to be
|
||||||
|
// called, and we respond with a nil-error.
|
||||||
|
case sendToSwitchSuccess:
|
||||||
|
select {
|
||||||
|
case sendResult <- nil:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("unable to send result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we expect the SendToSwitch method to be
|
||||||
|
// called, and we respond with a forwarding error
|
||||||
|
case sendToSwitchResultFailure:
|
||||||
|
select {
|
||||||
|
case sendResult <- htlcswitch.NewForwardingError(
|
||||||
|
&lnwire.FailTemporaryChannelFailure{},
|
||||||
|
1,
|
||||||
|
):
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("unable to send result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we expect the GetPaymentResult method
|
||||||
|
// to be called, and we respond with the preimage to
|
||||||
|
// complete the payment.
|
||||||
|
case getPaymentResultSuccess:
|
||||||
|
select {
|
||||||
|
case getPaymentResult <- &htlcswitch.PaymentResult{
|
||||||
|
Preimage: preImage,
|
||||||
|
}:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("unable to send result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this state we expect the GetPaymentResult method
|
||||||
|
// to be called, and we respond with a forwarding
|
||||||
|
// error, indicating that the router should retry.
|
||||||
|
case getPaymentResultTempFailure:
|
||||||
|
failure := htlcswitch.NewForwardingError(
|
||||||
|
&lnwire.FailTemporaryChannelFailure{},
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case getPaymentResult <- &htlcswitch.PaymentResult{
|
||||||
|
Error: failure,
|
||||||
|
}:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("unable to get result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this state we expect the router to call the
|
||||||
|
// GetPaymentResult method, and we will respond with a
|
||||||
|
// terminal error, indiating the router should stop
|
||||||
|
// making payment attempts.
|
||||||
|
case getPaymentResultTerminalFailure:
|
||||||
|
failure := htlcswitch.NewForwardingError(
|
||||||
|
&lnwire.FailIncorrectDetails{},
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case getPaymentResult <- &htlcswitch.PaymentResult{
|
||||||
|
Error: failure,
|
||||||
|
}:
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("unable to get result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we manually try to resend the same
|
||||||
|
// payment, making sure the router responds with an
|
||||||
|
// error indicating that it is already in flight.
|
||||||
|
case resendPayment:
|
||||||
|
resendResult = make(chan error)
|
||||||
|
go func() {
|
||||||
|
_, _, err := router.SendPayment(&payment)
|
||||||
|
resendResult <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
// In this step we manually stop the router.
|
||||||
|
case stopRouter:
|
||||||
|
select {
|
||||||
|
case getPaymentResultErr <- fmt.Errorf(
|
||||||
|
"shutting down"):
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("unable to send payment " +
|
||||||
|
"result error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Stop(); err != nil {
|
||||||
|
t.Fatalf("unable to restart: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this step we manually start the router.
|
||||||
|
case startRouter:
|
||||||
|
router, sendResult, getPaymentResult,
|
||||||
|
getPaymentResultErr = setupRouter()
|
||||||
|
|
||||||
|
// In this state we expect to receive an error for the
|
||||||
|
// original payment made.
|
||||||
|
case paymentError:
|
||||||
|
select {
|
||||||
|
case err := <-paymentResult:
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("got no payment result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this state we expect the original payment to
|
||||||
|
// succeed.
|
||||||
|
case paymentSuccess:
|
||||||
|
select {
|
||||||
|
case err := <-paymentResult:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("did not expect "+
|
||||||
|
"error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("got no payment result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this state we expect to receive an error for the
|
||||||
|
// resent payment made.
|
||||||
|
case resentPaymentError:
|
||||||
|
select {
|
||||||
|
case err := <-resendResult:
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("got no payment result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In this state we expect the resent payment to
|
||||||
|
// succeed.
|
||||||
|
case resentPaymentSuccess:
|
||||||
|
select {
|
||||||
|
case err := <-resendResult:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("did not expect error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(stepTimeout):
|
||||||
|
t.Fatalf("got no payment result")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Fatalf("unknown step %v", step)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-time.After(testTimeout):
|
||||||
|
t.Fatalf("SendPayment didn't exit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user