mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-13 15:49:04 +01:00
routing: delete old payment lifecycle related unit tests
The old payment lifecycle is removed due to it's not "unit" - maintaining these tests probably takes as much work as the actual methods being tested, if not more so. Moreover, the usage of the old mockers in current payment lifecycle test is removed as it re-implements other interfaces and sometimes implements it uniquely just for the tests. This is bad as, not only we need to work on the actual interface implementations and test them , but also re-implement them again in the test without testing them!
This commit is contained in:
@@ -3400,649 +3400,6 @@ func createDummyLightningPayment(t *testing.T,
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendMPPaymentSucceed tests that we can successfully send a MPPayment via
|
||||
// router.SendPayment. This test mainly focuses on testing the logic of the
|
||||
// method resumePayment is implemented as expected.
|
||||
func TestSendMPPaymentSucceed(t *testing.T) {
|
||||
const startingBlockHeight = 101
|
||||
|
||||
// Create mockers to initialize the router.
|
||||
controlTower := &mockControlTower{}
|
||||
sessionSource := &mockPaymentSessionSource{}
|
||||
missionControl := &mockMissionControl{}
|
||||
payer := &mockPaymentAttemptDispatcher{}
|
||||
chain := newMockChain(startingBlockHeight)
|
||||
chainView := newMockChainView(chain)
|
||||
testGraph := createDummyTestGraph(t)
|
||||
|
||||
// Define the behavior of the mockers to the point where we can
|
||||
// successfully start the router.
|
||||
controlTower.On("FetchInFlightPayments").Return(
|
||||
[]*channeldb.MPPayment{}, nil,
|
||||
)
|
||||
payer.On("CleanStore", mock.Anything).Return(nil)
|
||||
|
||||
// Create and start the router.
|
||||
router, err := New(Config{
|
||||
Control: controlTower,
|
||||
SessionSource: sessionSource,
|
||||
MissionControl: missionControl,
|
||||
Payer: payer,
|
||||
|
||||
// TODO(yy): create new mocks for the chain and chainview.
|
||||
Chain: chain,
|
||||
ChainView: chainView,
|
||||
|
||||
// TODO(yy): mock the graph once it's changed into interface.
|
||||
Graph: testGraph.graph,
|
||||
|
||||
Clock: clock.NewTestClock(time.Unix(1, 0)),
|
||||
GraphPruneInterval: time.Hour * 2,
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
next := atomic.AddUint64(&uniquePaymentID, 1)
|
||||
return next, nil
|
||||
},
|
||||
|
||||
IsAlias: func(scid lnwire.ShortChannelID) bool {
|
||||
return false
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "failed to create router")
|
||||
|
||||
// Make sure the router can start and stop without error.
|
||||
require.NoError(t, router.Start(), "router failed to start")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, router.Stop(), "router failed to stop")
|
||||
})
|
||||
|
||||
// Once the router is started, check that the mocked methods are called
|
||||
// as expected.
|
||||
controlTower.AssertExpectations(t)
|
||||
payer.AssertExpectations(t)
|
||||
|
||||
// Mock the methods to the point where we are inside the function
|
||||
// resumePayment.
|
||||
paymentAmt := lnwire.MilliSatoshi(10000)
|
||||
req := createDummyLightningPayment(
|
||||
t, testGraph.aliasMap["c"], paymentAmt,
|
||||
)
|
||||
identifier := lntypes.Hash(req.Identifier())
|
||||
session := &mockPaymentSession{}
|
||||
sessionSource.On("NewPaymentSession", req).Return(session, nil)
|
||||
controlTower.On("InitPayment", identifier, mock.Anything).Return(nil)
|
||||
// Mock the InFlightHTLCs.
|
||||
var (
|
||||
htlcs []channeldb.HTLCAttempt
|
||||
numAttempts atomic.Uint32
|
||||
settled atomic.Bool
|
||||
numParts = uint32(4)
|
||||
)
|
||||
|
||||
// Make a mock MPPayment.
|
||||
payment := &mockMPPayment{}
|
||||
payment.On("InFlightHTLCs").Return(htlcs).
|
||||
On("GetState").Return(&channeldb.MPPaymentState{FeesPaid: 0}).
|
||||
On("GetStatus").Return(channeldb.StatusInFlight)
|
||||
controlTower.On("FetchPayment", identifier).Return(payment, nil).Once()
|
||||
|
||||
// Mock FetchPayment to return the payment.
|
||||
controlTower.On("FetchPayment", identifier).Return(payment, nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
// When number of attempts made is less than 4, we will
|
||||
// mock the payment's methods to allow the lifecycle to
|
||||
// continue.
|
||||
if numAttempts.Load() < numParts {
|
||||
payment.On("AllowMoreAttempts").Return(true, nil).Once()
|
||||
return
|
||||
}
|
||||
|
||||
if !settled.Load() {
|
||||
fmt.Println("wait")
|
||||
payment.On("AllowMoreAttempts").Return(false, nil).Once()
|
||||
payment.On("NeedWaitAttempts").Return(true, nil).Once()
|
||||
// We add another attempt to the counter to
|
||||
// unblock next time.
|
||||
return
|
||||
}
|
||||
|
||||
payment.On("AllowMoreAttempts").Return(false, nil).
|
||||
On("NeedWaitAttempts").Return(false, nil)
|
||||
})
|
||||
|
||||
// Mock SettleAttempt.
|
||||
preimage := lntypes.Preimage{1, 2, 3}
|
||||
settledAttempt := makeSettledAttempt(
|
||||
int(paymentAmt/4), 0, preimage,
|
||||
)
|
||||
|
||||
controlTower.On("SettleAttempt",
|
||||
identifier, mock.Anything, mock.Anything,
|
||||
).Return(&settledAttempt, nil).Run(func(args mock.Arguments) {
|
||||
// We want to at least wait for one settlement.
|
||||
if numAttempts.Load() > 1 {
|
||||
settled.Store(true)
|
||||
}
|
||||
})
|
||||
|
||||
// Create a route that can send 1/4 of the total amount. This value
|
||||
// will be returned by calling RequestRoute.
|
||||
shard, err := createTestRoute(paymentAmt/4, testGraph.aliasMap)
|
||||
require.NoError(t, err, "failed to create route")
|
||||
|
||||
session.On("RequestRoute",
|
||||
mock.Anything, mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(shard, nil)
|
||||
|
||||
// Make a new htlc attempt with zero fee and append it to the payment's
|
||||
// HTLCs when calling RegisterAttempt.
|
||||
controlTower.On("RegisterAttempt",
|
||||
identifier, mock.Anything,
|
||||
).Return(nil).Run(func(args mock.Arguments) {
|
||||
numAttempts.Add(1)
|
||||
})
|
||||
|
||||
// Create a buffered chan and it will be returned by GetAttemptResult.
|
||||
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
|
||||
payer.On("GetAttemptResult",
|
||||
mock.Anything, identifier, mock.Anything,
|
||||
).Run(func(args mock.Arguments) {
|
||||
// Before the mock method is returned, we send the result to
|
||||
// the read-only chan.
|
||||
payer.resultChan <- &htlcswitch.PaymentResult{}
|
||||
})
|
||||
|
||||
// Simple mocking the rest.
|
||||
payer.On("SendHTLC",
|
||||
mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(nil)
|
||||
|
||||
missionControl.On("ReportPaymentSuccess",
|
||||
mock.Anything, mock.Anything,
|
||||
).Return(nil).Run(func(args mock.Arguments) {
|
||||
})
|
||||
|
||||
controlTower.On("DeleteFailedAttempts", identifier).Return(nil)
|
||||
|
||||
payment.On("TerminalInfo").Return(&settledAttempt, nil)
|
||||
|
||||
// Call the actual method SendPayment on router. This is place inside a
|
||||
// goroutine so we can set a timeout for the whole test, in case
|
||||
// anything goes wrong and the test never finishes.
|
||||
done := make(chan struct{})
|
||||
var p lntypes.Hash
|
||||
go func() {
|
||||
p, _, err = router.SendPayment(req)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(testTimeout):
|
||||
t.Fatalf("SendPayment didn't exit")
|
||||
}
|
||||
|
||||
// Finally, validate the returned values and check that the mock
|
||||
// methods are called as expected.
|
||||
require.NoError(t, err, "send payment failed")
|
||||
require.EqualValues(t, preimage, p, "preimage not match")
|
||||
|
||||
// Note that we also implicitly check the methods such as FailAttempt,
|
||||
// ReportPaymentFail, etc, are not called because we never mocked them
|
||||
// in this test. If any of the unexpected methods was called, the test
|
||||
// would fail.
|
||||
controlTower.AssertExpectations(t)
|
||||
payer.AssertExpectations(t)
|
||||
sessionSource.AssertExpectations(t)
|
||||
session.AssertExpectations(t)
|
||||
missionControl.AssertExpectations(t)
|
||||
payment.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// TestSendMPPaymentSucceedOnExtraShards tests that we need extra attempts if
|
||||
// there are failed ones,so that a payment is successfully sent. This test
|
||||
// mainly focuses on testing the logic of the method resumePayment is
|
||||
// implemented as expected.
|
||||
func TestSendMPPaymentSucceedOnExtraShards(t *testing.T) {
|
||||
const startingBlockHeight = 101
|
||||
|
||||
// Create mockers to initialize the router.
|
||||
controlTower := &mockControlTower{}
|
||||
sessionSource := &mockPaymentSessionSource{}
|
||||
missionControl := &mockMissionControl{}
|
||||
payer := &mockPaymentAttemptDispatcher{}
|
||||
chain := newMockChain(startingBlockHeight)
|
||||
chainView := newMockChainView(chain)
|
||||
testGraph := createDummyTestGraph(t)
|
||||
|
||||
// Define the behavior of the mockers to the point where we can
|
||||
// successfully start the router.
|
||||
controlTower.On("FetchInFlightPayments").Return(
|
||||
[]*channeldb.MPPayment{}, nil,
|
||||
)
|
||||
payer.On("CleanStore", mock.Anything).Return(nil)
|
||||
|
||||
// Create and start the router.
|
||||
router, err := New(Config{
|
||||
Control: controlTower,
|
||||
SessionSource: sessionSource,
|
||||
MissionControl: missionControl,
|
||||
Payer: payer,
|
||||
|
||||
// TODO(yy): create new mocks for the chain and chainview.
|
||||
Chain: chain,
|
||||
ChainView: chainView,
|
||||
|
||||
// TODO(yy): mock the graph once it's changed into interface.
|
||||
Graph: testGraph.graph,
|
||||
|
||||
Clock: clock.NewTestClock(time.Unix(1, 0)),
|
||||
GraphPruneInterval: time.Hour * 2,
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
next := atomic.AddUint64(&uniquePaymentID, 1)
|
||||
return next, nil
|
||||
},
|
||||
|
||||
IsAlias: func(scid lnwire.ShortChannelID) bool {
|
||||
return false
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "failed to create router")
|
||||
|
||||
// Make sure the router can start and stop without error.
|
||||
require.NoError(t, router.Start(), "router failed to start")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, router.Stop(), "router failed to stop")
|
||||
})
|
||||
|
||||
// Once the router is started, check that the mocked methods are called
|
||||
// as expected.
|
||||
controlTower.AssertExpectations(t)
|
||||
payer.AssertExpectations(t)
|
||||
|
||||
// Mock the methods to the point where we are inside the function
|
||||
// resumePayment.
|
||||
paymentAmt := lnwire.MilliSatoshi(20000)
|
||||
req := createDummyLightningPayment(
|
||||
t, testGraph.aliasMap["c"], paymentAmt,
|
||||
)
|
||||
identifier := lntypes.Hash(req.Identifier())
|
||||
session := &mockPaymentSession{}
|
||||
sessionSource.On("NewPaymentSession", req).Return(session, nil)
|
||||
controlTower.On("InitPayment", identifier, mock.Anything).Return(nil)
|
||||
|
||||
// Mock the InFlightHTLCs.
|
||||
var (
|
||||
htlcs []channeldb.HTLCAttempt
|
||||
numAttempts atomic.Uint32
|
||||
failAttemptCount atomic.Uint32
|
||||
settled atomic.Bool
|
||||
)
|
||||
|
||||
// Make a mock MPPayment.
|
||||
payment := &mockMPPayment{}
|
||||
payment.On("InFlightHTLCs").Return(htlcs).
|
||||
On("GetState").Return(&channeldb.MPPaymentState{FeesPaid: 0}).
|
||||
On("GetStatus").Return(channeldb.StatusInFlight)
|
||||
controlTower.On("FetchPayment", identifier).Return(payment, nil).Once()
|
||||
|
||||
// Mock FetchPayment to return the payment.
|
||||
controlTower.On("FetchPayment", identifier).Return(payment, nil).
|
||||
Run(func(args mock.Arguments) {
|
||||
// When number of attempts made is less than 4, we will
|
||||
// mock the payment's methods to allow the lifecycle to
|
||||
// continue.
|
||||
attempts := numAttempts.Load()
|
||||
if attempts < 6 {
|
||||
payment.On("AllowMoreAttempts").Return(true, nil).Once()
|
||||
return
|
||||
}
|
||||
|
||||
if !settled.Load() {
|
||||
payment.On("AllowMoreAttempts").Return(false, nil).Once()
|
||||
payment.On("NeedWaitAttempts").Return(true, nil).Once()
|
||||
// We add another attempt to the counter to
|
||||
// unblock next time.
|
||||
numAttempts.Add(1)
|
||||
return
|
||||
}
|
||||
|
||||
payment.On("AllowMoreAttempts").Return(false, nil).
|
||||
On("NeedWaitAttempts").Return(false, nil)
|
||||
})
|
||||
|
||||
// Create a route that can send 1/4 of the total amount. This value
|
||||
// will be returned by calling RequestRoute.
|
||||
shard, err := createTestRoute(paymentAmt/4, testGraph.aliasMap)
|
||||
require.NoError(t, err, "failed to create route")
|
||||
session.On("RequestRoute",
|
||||
mock.Anything, mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(shard, nil)
|
||||
|
||||
// Make a new htlc attempt with zero fee and append it to the payment's
|
||||
// HTLCs when calling RegisterAttempt.
|
||||
controlTower.On("RegisterAttempt",
|
||||
identifier, mock.Anything,
|
||||
).Return(nil).Run(func(args mock.Arguments) {
|
||||
// Increase the counter whenever an attempt is made.
|
||||
numAttempts.Add(1)
|
||||
})
|
||||
|
||||
// Create a buffered chan and it will be returned by GetAttemptResult.
|
||||
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
|
||||
|
||||
// We use the failAttemptCount to track how many attempts we want to
|
||||
// fail. Each time the following mock method is called, the count gets
|
||||
// updated.
|
||||
payer.On("GetAttemptResult",
|
||||
mock.Anything, identifier, mock.Anything,
|
||||
).Run(func(args mock.Arguments) {
|
||||
// Before the mock method is returned, we send the result to
|
||||
// the read-only chan.
|
||||
|
||||
// Update the counter.
|
||||
failAttemptCount.Add(1)
|
||||
|
||||
// We will make the first two attempts failed with temporary
|
||||
// error.
|
||||
if failAttemptCount.Load() <= 2 {
|
||||
payer.resultChan <- &htlcswitch.PaymentResult{
|
||||
Error: htlcswitch.NewForwardingError(
|
||||
&lnwire.FailTemporaryChannelFailure{},
|
||||
1,
|
||||
),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise we will mark the attempt succeeded.
|
||||
payer.resultChan <- &htlcswitch.PaymentResult{}
|
||||
})
|
||||
|
||||
// Mock the FailAttempt method to fail one of the attempts.
|
||||
var failedAttempt channeldb.HTLCAttempt
|
||||
controlTower.On("FailAttempt",
|
||||
identifier, mock.Anything, mock.Anything,
|
||||
).Return(&failedAttempt, nil)
|
||||
|
||||
// Setup ReportPaymentFail to return nil reason and error so the
|
||||
// payment won't fail.
|
||||
missionControl.On("ReportPaymentFail",
|
||||
mock.Anything, mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(nil, nil)
|
||||
|
||||
// Simple mocking the rest.
|
||||
payer.On("SendHTLC",
|
||||
mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(nil)
|
||||
missionControl.On("ReportPaymentSuccess",
|
||||
mock.Anything, mock.Anything,
|
||||
).Return(nil)
|
||||
|
||||
// Mock SettleAttempt by changing one of the HTLCs to be settled.
|
||||
preimage := lntypes.Preimage{1, 2, 3}
|
||||
settledAttempt := makeSettledAttempt(
|
||||
int(paymentAmt/4), 0, preimage,
|
||||
)
|
||||
controlTower.On("SettleAttempt",
|
||||
identifier, mock.Anything, mock.Anything,
|
||||
).Return(&settledAttempt, nil).Run(func(args mock.Arguments) {
|
||||
if numAttempts.Load() > 1 {
|
||||
settled.Store(true)
|
||||
}
|
||||
})
|
||||
|
||||
controlTower.On("DeleteFailedAttempts", identifier).Return(nil)
|
||||
|
||||
payment.On("TerminalInfo").Return(&settledAttempt, nil)
|
||||
|
||||
// Call the actual method SendPayment on router. This is place inside a
|
||||
// goroutine so we can set a timeout for the whole test, in case
|
||||
// anything goes wrong and the test never finishes.
|
||||
done := make(chan struct{})
|
||||
var p lntypes.Hash
|
||||
go func() {
|
||||
p, _, err = router.SendPayment(req)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(testTimeout):
|
||||
t.Fatalf("SendPayment didn't exit")
|
||||
}
|
||||
|
||||
// Finally, validate the returned values and check that the mock
|
||||
// methods are called as expected.
|
||||
require.NoError(t, err, "send payment failed")
|
||||
require.EqualValues(t, preimage, p, "preimage not match")
|
||||
|
||||
controlTower.AssertExpectations(t)
|
||||
payer.AssertExpectations(t)
|
||||
sessionSource.AssertExpectations(t)
|
||||
session.AssertExpectations(t)
|
||||
missionControl.AssertExpectations(t)
|
||||
payment.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// TestSendMPPaymentFailed tests that when one of the shard fails with a
|
||||
// terminal error, the router will stop attempting and the payment will fail.
|
||||
// This test mainly focuses on testing the logic of the method resumePayment
|
||||
// is implemented as expected.
|
||||
func TestSendMPPaymentFailed(t *testing.T) {
|
||||
const startingBlockHeight = 101
|
||||
|
||||
// Create mockers to initialize the router.
|
||||
controlTower := &mockControlTower{}
|
||||
sessionSource := &mockPaymentSessionSource{}
|
||||
missionControl := &mockMissionControl{}
|
||||
payer := &mockPaymentAttemptDispatcher{}
|
||||
chain := newMockChain(startingBlockHeight)
|
||||
chainView := newMockChainView(chain)
|
||||
testGraph := createDummyTestGraph(t)
|
||||
|
||||
// Define the behavior of the mockers to the point where we can
|
||||
// successfully start the router.
|
||||
controlTower.On("FetchInFlightPayments").Return(
|
||||
[]*channeldb.MPPayment{}, nil,
|
||||
)
|
||||
payer.On("CleanStore", mock.Anything).Return(nil)
|
||||
|
||||
// Create and start the router.
|
||||
router, err := New(Config{
|
||||
Control: controlTower,
|
||||
SessionSource: sessionSource,
|
||||
MissionControl: missionControl,
|
||||
Payer: payer,
|
||||
|
||||
// TODO(yy): create new mocks for the chain and chainview.
|
||||
Chain: chain,
|
||||
ChainView: chainView,
|
||||
|
||||
// TODO(yy): mock the graph once it's changed into interface.
|
||||
Graph: testGraph.graph,
|
||||
|
||||
Clock: clock.NewTestClock(time.Unix(1, 0)),
|
||||
GraphPruneInterval: time.Hour * 2,
|
||||
NextPaymentID: func() (uint64, error) {
|
||||
next := atomic.AddUint64(&uniquePaymentID, 1)
|
||||
return next, nil
|
||||
},
|
||||
|
||||
IsAlias: func(scid lnwire.ShortChannelID) bool {
|
||||
return false
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "failed to create router")
|
||||
|
||||
// Make sure the router can start and stop without error.
|
||||
require.NoError(t, router.Start(), "router failed to start")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, router.Stop(), "router failed to stop")
|
||||
})
|
||||
|
||||
// Once the router is started, check that the mocked methods are called
|
||||
// as expected.
|
||||
controlTower.AssertExpectations(t)
|
||||
payer.AssertExpectations(t)
|
||||
|
||||
// Mock the methods to the point where we are inside the function
|
||||
// resumePayment.
|
||||
paymentAmt := lnwire.MilliSatoshi(10000)
|
||||
req := createDummyLightningPayment(
|
||||
t, testGraph.aliasMap["c"], paymentAmt,
|
||||
)
|
||||
identifier := lntypes.Hash(req.Identifier())
|
||||
session := &mockPaymentSession{}
|
||||
sessionSource.On("NewPaymentSession", req).Return(session, nil)
|
||||
controlTower.On("InitPayment", identifier, mock.Anything).Return(nil)
|
||||
|
||||
// Mock the InFlightHTLCs.
|
||||
var (
|
||||
htlcs []channeldb.HTLCAttempt
|
||||
numAttempts atomic.Uint32
|
||||
failAttemptCount atomic.Uint32
|
||||
failed atomic.Bool
|
||||
numParts = uint32(4)
|
||||
)
|
||||
|
||||
// Make a mock MPPayment.
|
||||
payment := &mockMPPayment{}
|
||||
payment.On("InFlightHTLCs").Return(htlcs).Once()
|
||||
payment.On("GetState").Return(&channeldb.MPPaymentState{}).
|
||||
On("GetStatus").Return(channeldb.StatusInFlight)
|
||||
controlTower.On("FetchPayment", identifier).Return(payment, nil).Once()
|
||||
|
||||
// Mock the sequential FetchPayment to return the payment.
|
||||
controlTower.On("FetchPayment", identifier).Return(payment, nil).Run(
|
||||
func(_ mock.Arguments) {
|
||||
// We want to at least send out all parts in order to
|
||||
// wait for them later.
|
||||
if numAttempts.Load() < numParts {
|
||||
payment.On("AllowMoreAttempts").Return(true, nil).Once()
|
||||
return
|
||||
}
|
||||
|
||||
// Wait if the payment wasn't failed yet.
|
||||
if !failed.Load() {
|
||||
payment.On("AllowMoreAttempts").Return(false, nil).Once().
|
||||
On("NeedWaitAttempts").Return(true, nil).Once()
|
||||
return
|
||||
}
|
||||
|
||||
payment.On("AllowMoreAttempts").Return(false, nil).
|
||||
On("NeedWaitAttempts").Return(false, nil).Once()
|
||||
})
|
||||
|
||||
// Create a route that can send 1/4 of the total amount. This value
|
||||
// will be returned by calling RequestRoute.
|
||||
shard, err := createTestRoute(paymentAmt/4, testGraph.aliasMap)
|
||||
require.NoError(t, err, "failed to create route")
|
||||
session.On("RequestRoute",
|
||||
mock.Anything, mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(shard, nil)
|
||||
|
||||
// Make a new htlc attempt with zero fee and append it to the payment's
|
||||
// HTLCs when calling RegisterAttempt.
|
||||
controlTower.On("RegisterAttempt",
|
||||
identifier, mock.Anything,
|
||||
).Return(nil).Run(func(args mock.Arguments) {
|
||||
numAttempts.Add(1)
|
||||
})
|
||||
|
||||
// Create a buffered chan and it will be returned by GetAttemptResult.
|
||||
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
|
||||
|
||||
// We use the failAttemptCount to track how many attempts we want to
|
||||
// fail. Each time the following mock method is called, the count gets
|
||||
// updated.
|
||||
payer.On("GetAttemptResult",
|
||||
mock.Anything, identifier, mock.Anything,
|
||||
).Run(func(_ mock.Arguments) {
|
||||
// Before the mock method is returned, we send the result to
|
||||
// the read-only chan.
|
||||
|
||||
// Update the counter.
|
||||
failAttemptCount.Add(1)
|
||||
|
||||
// We fail the first attempt with terminal error.
|
||||
if failAttemptCount.Load() == 1 {
|
||||
payer.resultChan <- &htlcswitch.PaymentResult{
|
||||
Error: htlcswitch.NewForwardingError(
|
||||
&lnwire.FailIncorrectDetails{},
|
||||
1,
|
||||
),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// We will make the rest attempts failed with temporary error.
|
||||
payer.resultChan <- &htlcswitch.PaymentResult{
|
||||
Error: htlcswitch.NewForwardingError(
|
||||
&lnwire.FailTemporaryChannelFailure{},
|
||||
1,
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
// Mock the FailAttempt method to fail one of the attempts.
|
||||
var failedAttempt channeldb.HTLCAttempt
|
||||
controlTower.On("FailAttempt",
|
||||
identifier, mock.Anything, mock.Anything,
|
||||
).Return(&failedAttempt, nil)
|
||||
|
||||
// Setup ReportPaymentFail to return nil reason and error so the
|
||||
// payment won't fail.
|
||||
failureReason := channeldb.FailureReasonPaymentDetails
|
||||
missionControl.On("ReportPaymentFail",
|
||||
mock.Anything, mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(&failureReason, nil)
|
||||
|
||||
// Simple mocking the rest.
|
||||
controlTower.On("FailPayment",
|
||||
identifier, failureReason,
|
||||
).Return(nil).Run(func(_ mock.Arguments) {
|
||||
failed.Store(true)
|
||||
})
|
||||
|
||||
// Mock the payment to return the failure reason.
|
||||
payer.On("SendHTLC",
|
||||
mock.Anything, mock.Anything, mock.Anything,
|
||||
).Return(nil)
|
||||
|
||||
payment.On("TerminalInfo").Return(nil, &failureReason)
|
||||
|
||||
controlTower.On("DeleteFailedAttempts", identifier).Return(nil)
|
||||
|
||||
// Call the actual method SendPayment on router. This is place inside a
|
||||
// goroutine so we can set a timeout for the whole test, in case
|
||||
// anything goes wrong and the test never finishes.
|
||||
done := make(chan struct{})
|
||||
var p lntypes.Hash
|
||||
go func() {
|
||||
p, _, err = router.SendPayment(req)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(testTimeout):
|
||||
t.Fatalf("SendPayment didn't exit")
|
||||
}
|
||||
|
||||
// Finally, validate the returned values and check that the mock
|
||||
// methods are called as expected.
|
||||
require.Error(t, err, "expected send payment error")
|
||||
require.EqualValues(t, [32]byte{}, p, "preimage not match")
|
||||
require.GreaterOrEqual(t, failAttemptCount.Load(), uint32(1))
|
||||
|
||||
controlTower.AssertExpectations(t)
|
||||
payer.AssertExpectations(t)
|
||||
sessionSource.AssertExpectations(t)
|
||||
session.AssertExpectations(t)
|
||||
missionControl.AssertExpectations(t)
|
||||
payment.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// TestBlockDifferenceFix tests if when the router is behind on blocks, the
|
||||
// router catches up to the best block head.
|
||||
func TestBlockDifferenceFix(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user