lnd/payments/db/sql_store_test.go

229 lines
4.9 KiB
Go

//go:build test_db_sqlite || test_db_postgres
package paymentsdb
import (
"database/sql"
"testing"
"github.com/stretchr/testify/require"
)
// TestComputePaymentStatus tests the SQL to domain type conversion logic in
// computePaymentStatusFromResolutions. This is a pure unit test with no
// database interaction. However the function is only used in the SQL store and
// used sql data types so we test it in a sql specific file.
func TestComputePaymentStatus(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
resolutionTypes []sql.NullInt32
failReason sql.NullInt32
expectedStatus PaymentStatus
expectError bool
}{
{
name: "all NULL resolutions means in-flight",
resolutionTypes: []sql.NullInt32{
{Valid: false}, // NULL = in-flight
{Valid: false},
},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusInFlight,
},
{
name: "settled resolution without fail reason",
resolutionTypes: []sql.NullInt32{{
Int32: int32(HTLCAttemptResolutionSettled),
Valid: true,
}},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusSucceeded,
},
{
name: "failed resolution without fail reason",
resolutionTypes: []sql.NullInt32{{
Int32: int32(HTLCAttemptResolutionFailed),
Valid: true,
}},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusInFlight,
},
{
name: "failed resolution with fail reason",
resolutionTypes: []sql.NullInt32{{
Int32: int32(HTLCAttemptResolutionFailed),
Valid: true,
}},
failReason: sql.NullInt32{
Int32: int32(FailureReasonNoRoute),
Valid: true,
},
expectedStatus: StatusFailed,
},
{
name: "mixed: in-flight and settled",
resolutionTypes: []sql.NullInt32{
{Valid: false}, // in-flight
{
Int32: int32(
HTLCAttemptResolutionSettled,
),
Valid: true,
},
},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusInFlight,
},
{
name: "mixed: in-flight and failed",
resolutionTypes: []sql.NullInt32{
{Valid: false}, // in-flight
{
Int32: int32(
HTLCAttemptResolutionFailed,
),
Valid: true,
},
},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusInFlight,
},
{
name: "mixed: settled and failed",
resolutionTypes: []sql.NullInt32{
{
Int32: int32(
HTLCAttemptResolutionSettled,
),
Valid: true,
},
{
Int32: int32(
HTLCAttemptResolutionFailed,
),
Valid: true,
},
},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusSucceeded,
},
{
name: "no resolutions, no fail reason, " +
"means initiated",
resolutionTypes: []sql.NullInt32{},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusInitiated,
},
{
name: "no resolutions with fail reason, " +
"means failed",
resolutionTypes: []sql.NullInt32{},
failReason: sql.NullInt32{
Int32: int32(FailureReasonNoRoute),
Valid: true,
},
expectedStatus: StatusFailed,
},
{
name: "unknown resolution type returns error",
resolutionTypes: []sql.NullInt32{
{Int32: 999, Valid: true}, // invalid type
},
failReason: sql.NullInt32{Valid: false},
expectError: true,
},
{
name: "all three states: in-flight, settled, failed",
resolutionTypes: []sql.NullInt32{
{
Valid: false, // in-flight
},
{
Int32: int32(
HTLCAttemptResolutionSettled,
),
Valid: true,
},
{
Int32: int32(
HTLCAttemptResolutionFailed,
),
Valid: true,
},
},
failReason: sql.NullInt32{
Int32: int32(FailureReasonTimeout),
Valid: true,
},
expectedStatus: StatusInFlight,
},
{
name: "multiple settled HTLCs",
resolutionTypes: []sql.NullInt32{
{
Int32: int32(
HTLCAttemptResolutionSettled,
),
Valid: true,
},
{
Int32: int32(
HTLCAttemptResolutionSettled,
),
Valid: true,
},
{
Int32: int32(
HTLCAttemptResolutionSettled,
),
Valid: true,
},
},
failReason: sql.NullInt32{Valid: false},
expectedStatus: StatusSucceeded,
},
{
name: "multiple failed HTLCs with fail reason",
resolutionTypes: []sql.NullInt32{
{
Int32: int32(
HTLCAttemptResolutionFailed,
),
Valid: true,
},
{
Int32: int32(
HTLCAttemptResolutionFailed,
),
Valid: true,
},
},
failReason: sql.NullInt32{
Int32: int32(FailureReasonNoRoute),
Valid: true,
},
expectedStatus: StatusFailed,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
status, err := computePaymentStatusFromResolutions(
tc.resolutionTypes, tc.failReason,
)
if tc.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.expectedStatus, status,
"got %s, want %s", status, tc.expectedStatus)
})
}
}