multi: move payment related code into own package

This commit moves most of the code into its own package. It is
the smallest code move possible without moving import cycles and
keeping the changes to the code base as small as possible during
refactor.
This commit is contained in:
ziggie
2025-08-13 09:19:20 +02:00
parent 77a6b577eb
commit 03af9858d2
26 changed files with 1075 additions and 948 deletions

View File

@@ -0,0 +1,249 @@
package paymentsdb
import (
"fmt"
"testing"
"github.com/lightningnetwork/lnd/channeldb"
"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 := channeldb.FailureReasonNoRoute
failure := &reason
testCases := []struct {
name string
htlcs []HTLCAttempt
reason *channeldb.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)
})
}
}