From fac60445017ea4a21cf15912eec73d260f892457 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Tue, 7 Feb 2023 13:09:23 +0800 Subject: [PATCH] channeldb: add unit test for `decidePaymentStatus` --- channeldb/payment_status_test.go | 248 +++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 channeldb/payment_status_test.go diff --git a/channeldb/payment_status_test.go b/channeldb/payment_status_test.go new file mode 100644 index 000000000..f82162319 --- /dev/null +++ b/channeldb/payment_status_test.go @@ -0,0 +1,248 @@ +package channeldb + +import ( + "fmt" + "testing" + + "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" +) + +// TestDecidePaymentStatus checks that given a set of HTLC and a failure +// reason, the payment's current status is returned as expected. +func TestDecidePaymentStatus(t *testing.T) { + t.Parallel() + + // Create two attempts used for testing. + inflight := HTLCAttempt{} + settled := HTLCAttempt{ + Settle: &HTLCSettleInfo{Preimage: lntypes.Preimage{}}, + } + failed := HTLCAttempt{ + Failure: &HTLCFailInfo{FailureSourceIndex: 1}, + } + + // Create a test failure reason and get the pointer. + reason := FailureReasonNoRoute + failure := &reason + + testCases := []struct { + name string + htlcs []HTLCAttempt + reason *FailureReason + expectedStatus PaymentStatus + expectedErr error + }{ + { + // Test when inflight=true, settled=true, failed=true, + // reason=yes. + name: "state 1111", + htlcs: []HTLCAttempt{ + inflight, settled, failed, + }, + reason: failure, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=true, failed=true, + // reason=no. + name: "state 1110", + htlcs: []HTLCAttempt{ + inflight, settled, failed, + }, + reason: nil, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=true, failed=false, + // reason=yes. + name: "state 1101", + htlcs: []HTLCAttempt{inflight, settled}, + reason: failure, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=true, failed=false, + // reason=no. + name: "state 1100", + htlcs: []HTLCAttempt{inflight, settled}, + reason: nil, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=false, failed=true, + // reason=yes. + name: "state 1011", + htlcs: []HTLCAttempt{inflight, failed}, + reason: failure, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=false, failed=true, + // reason=no. + name: "state 1010", + htlcs: []HTLCAttempt{inflight, failed}, + reason: nil, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=false, failed=false, + // reason=yes. + name: "state 1001", + htlcs: []HTLCAttempt{inflight}, + reason: failure, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=true, settled=false, failed=false, + // reason=no. + name: "state 1000", + htlcs: []HTLCAttempt{inflight}, + reason: nil, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=false, settled=true, failed=true, + // reason=yes. + name: "state 0111", + htlcs: []HTLCAttempt{settled, failed}, + reason: failure, + expectedStatus: StatusSucceeded, + }, + { + // Test when inflight=false, settled=true, failed=true, + // reason=no. + name: "state 0110", + htlcs: []HTLCAttempt{settled, failed}, + reason: nil, + expectedStatus: StatusSucceeded, + }, + { + // Test when inflight=false, settled=true, + // failed=false, reason=yes. + name: "state 0101", + htlcs: []HTLCAttempt{settled}, + reason: failure, + expectedStatus: StatusSucceeded, + }, + { + // Test when inflight=false, settled=true, + // failed=false, reason=no. + name: "state 0100", + htlcs: []HTLCAttempt{settled}, + reason: nil, + expectedStatus: StatusSucceeded, + }, + { + // Test when inflight=false, settled=false, + // failed=true, reason=yes. + name: "state 0011", + htlcs: []HTLCAttempt{failed}, + reason: failure, + expectedStatus: StatusFailed, + }, + { + // Test when inflight=false, settled=false, + // failed=true, reason=no. + name: "state 0010", + htlcs: []HTLCAttempt{failed}, + reason: nil, + expectedStatus: StatusInFlight, + }, + { + // Test when inflight=false, settled=false, + // failed=false, reason=yes. + name: "state 0001", + htlcs: []HTLCAttempt{}, + reason: failure, + expectedStatus: StatusFailed, + }, + { + // Test when inflight=false, settled=false, + // failed=false, reason=no. + name: "state 0000", + htlcs: []HTLCAttempt{}, + reason: nil, + expectedStatus: StatusInitiated, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + status, err := decidePaymentStatus(tc.htlcs, tc.reason) + require.Equalf(t, tc.expectedStatus, status, + "got %s, want %s", status, tc.expectedStatus) + require.ErrorIs(t, err, tc.expectedErr) + }) + } +} + +// TestPaymentStatusActions checks whether a list of actions can be applied +// against ALL possible payment statuses. Unlike normal unit tests where we +// check against a single function, all the actions including `removable`, +// `initable`, and `updatable` are tested together so this test can be used as +// a reference of state transition. +func TestPaymentStatusActions(t *testing.T) { + t.Parallel() + + testCases := []struct { + status PaymentStatus + initErr error + updateErr error + removeErr error + }{ + { + status: StatusInitiated, + initErr: ErrPaymentExists, + updateErr: nil, + removeErr: nil, + }, + { + status: StatusInFlight, + initErr: ErrPaymentInFlight, + updateErr: nil, + removeErr: ErrPaymentInFlight, + }, + { + status: StatusSucceeded, + initErr: ErrAlreadyPaid, + updateErr: ErrPaymentAlreadySucceeded, + removeErr: nil, + }, + { + status: StatusFailed, + initErr: nil, + updateErr: ErrPaymentAlreadyFailed, + removeErr: nil, + }, + { + status: 0, + initErr: ErrUnknownPaymentStatus, + updateErr: ErrUnknownPaymentStatus, + removeErr: ErrUnknownPaymentStatus, + }, + } + + for i, tc := range testCases { + i, tc := i, tc + + ps := tc.status + name := fmt.Sprintf("test_%d_%s", i, ps.String()) + t.Run(name, func(t *testing.T) { + t.Parallel() + + require.ErrorIs(t, ps.initializable(), tc.initErr, + "initable under state %v", tc.status) + + require.ErrorIs(t, ps.updatable(), tc.updateErr, + "updatable under state %v", tc.status) + + require.ErrorIs(t, ps.removable(), tc.removeErr, + "removable under state %v", tc.status) + }) + } +}