diff --git a/liquidity/autoloop_test.go b/liquidity/autoloop_test.go index bcc07a5..253b12b 100644 --- a/liquidity/autoloop_test.go +++ b/liquidity/autoloop_test.go @@ -199,16 +199,30 @@ func TestAutoLoopEnabled(t *testing.T) { }, }, } + + singleLoopOut = &loopdb.LoopOut{ + Loop: loopdb.Loop{ + Events: []*loopdb.LoopEvent{ + { + SwapStateData: loopdb.SwapStateData{ + State: loopdb.StateInitiated, + }, + }, + }, + }, + } ) // Tick our autolooper with no existing swaps, we expect a loop out // swap to be dispatched for each channel. step := &autoloopStep{ - minAmt: 1, - maxAmt: amt + 1, - quotesOut: quotes, - expectedOut: loopOuts, + minAmt: 1, + maxAmt: amt + 1, + quotesOut: quotes, + expectedOut: loopOuts, + existingOutSingle: singleLoopOut, } + c.autoloop(step) // Tick again with both of our swaps in progress. We haven't shifted our @@ -220,9 +234,10 @@ func TestAutoLoopEnabled(t *testing.T) { } step = &autoloopStep{ - minAmt: 1, - maxAmt: amt + 1, - existingOut: existing, + minAmt: 1, + maxAmt: amt + 1, + existingOut: existing, + existingOutSingle: singleLoopOut, } c.autoloop(step) @@ -278,11 +293,12 @@ func TestAutoLoopEnabled(t *testing.T) { // still has balances which reflect that we need to swap), but nothing // for channel 2, since it has had a failure. step = &autoloopStep{ - minAmt: 1, - maxAmt: amt + 1, - existingOut: existing, - quotesOut: quotes, - expectedOut: loopOuts, + minAmt: 1, + maxAmt: amt + 1, + existingOut: existing, + quotesOut: quotes, + expectedOut: loopOuts, + existingOutSingle: singleLoopOut, } c.autoloop(step) @@ -299,10 +315,11 @@ func TestAutoLoopEnabled(t *testing.T) { } step = &autoloopStep{ - minAmt: 1, - maxAmt: amt + 1, - existingOut: existing, - quotesOut: quotes, + minAmt: 1, + maxAmt: amt + 1, + existingOut: existing, + quotesOut: quotes, + existingOutSingle: singleLoopOut, } c.autoloop(step) @@ -446,13 +463,27 @@ func TestAutoloopAddress(t *testing.T) { }, }, } + + singleLoopOut = &loopdb.LoopOut{ + Loop: loopdb.Loop{ + Events: []*loopdb.LoopEvent{ + { + SwapStateData: loopdb.SwapStateData{ + State: loopdb.StateHtlcPublished, + }, + }, + }, + }, + } ) step := &autoloopStep{ - minAmt: 1, - maxAmt: amt + 1, - quotesOut: quotes, - expectedOut: loopOuts, + minAmt: 1, + maxAmt: amt + 1, + quotesOut: quotes, + expectedOut: loopOuts, + existingOutSingle: singleLoopOut, + keepDestAddr: true, } c.autoloop(step) @@ -606,6 +637,18 @@ func TestCompositeRules(t *testing.T) { }, }, } + + singleLoopOut = &loopdb.LoopOut{ + Loop: loopdb.Loop{ + Events: []*loopdb.LoopEvent{ + { + SwapStateData: loopdb.SwapStateData{ + State: loopdb.StateHtlcPublished, + }, + }, + }, + }, + } ) // Tick our autolooper with no existing swaps, we expect a loop out @@ -613,10 +656,11 @@ func TestCompositeRules(t *testing.T) { // maximum to be greater than the swap amount for our peer swap (which // is the larger of the two swaps). step := &autoloopStep{ - minAmt: 1, - maxAmt: peerAmount + 1, - quotesOut: quotes, - expectedOut: loopOuts, + minAmt: 1, + maxAmt: peerAmount + 1, + quotesOut: quotes, + expectedOut: loopOuts, + existingOutSingle: singleLoopOut, } c.autoloop(step) @@ -928,6 +972,18 @@ func TestAutoloopBothTypes(t *testing.T) { Label: labels.AutoloopLabel(swap.TypeIn), Initiator: autoloopSwapInitiator, } + + singleLoopOut = &loopdb.LoopOut{ + Loop: loopdb.Loop{ + Events: []*loopdb.LoopEvent{ + { + SwapStateData: loopdb.SwapStateData{ + State: loopdb.StateHtlcPublished, + }, + }, + }, + }, + } ) step := &autoloopStep{ @@ -961,6 +1017,7 @@ func TestAutoloopBothTypes(t *testing.T) { }, }, }, + existingOutSingle: singleLoopOut, } c.autoloop(step) c.stop() diff --git a/liquidity/autoloop_testcontext_test.go b/liquidity/autoloop_testcontext_test.go index bf25ab1..8379fa9 100644 --- a/liquidity/autoloop_testcontext_test.go +++ b/liquidity/autoloop_testcontext_test.go @@ -2,7 +2,9 @@ package liquidity import ( "context" + "reflect" "testing" + "time" "github.com/btcsuite/btcd/btcutil" "github.com/lightninglabs/lndclient" @@ -11,8 +13,10 @@ import ( "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/test" "github.com/lightningnetwork/lnd/clock" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/ticker" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type autoloopTestCtx struct { @@ -45,9 +49,17 @@ type autoloopTestCtx struct { // loopOuts is a channel that we get existing loop out swaps on. loopOuts chan []*loopdb.LoopOut + // loopOutSingle is the single loop out returned from fetching a single + // swap from store. + loopOutSingle *loopdb.LoopOut + // loopIns is a channel that we get existing loop in swaps on. loopIns chan []*loopdb.LoopIn + // loopInSingle is the single loop in returned from fetching a single + // swap from store. + loopInSingle *loopdb.LoopIn + // restrictions is a channel that we get swap restrictions on. restrictions chan *Restrictions @@ -131,6 +143,9 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, ListLoopOut: func() ([]*loopdb.LoopOut, error) { return <-testCtx.loopOuts, nil }, + GetLoopOut: func(hash lntypes.Hash) (*loopdb.LoopOut, error) { + return testCtx.loopOutSingle, nil + }, ListLoopIn: func() ([]*loopdb.LoopIn, error) { return <-testCtx.loopIns, nil }, @@ -188,6 +203,10 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters, testCtx.manager = NewManager(cfg) err := testCtx.manager.setParameters(context.Background(), parameters) assert.NoError(t, err) + // Override the payments check interval for the tests in order to not + // timeout. + testCtx.manager.params.CustomPaymentCheckInterval = + 150 * time.Millisecond <-done return testCtx } @@ -241,14 +260,17 @@ type loopInRequestResp struct { // autoloopStep contains all of the information to required to step // through an autoloop tick. type autoloopStep struct { - minAmt btcutil.Amount - maxAmt btcutil.Amount - existingOut []*loopdb.LoopOut - existingIn []*loopdb.LoopIn - quotesOut []quoteRequestResp - quotesIn []quoteInRequestResp - expectedOut []loopOutRequestResp - expectedIn []loopInRequestResp + minAmt btcutil.Amount + maxAmt btcutil.Amount + existingOut []*loopdb.LoopOut + existingOutSingle *loopdb.LoopOut + existingIn []*loopdb.LoopIn + existingInSingle *loopdb.LoopIn + quotesOut []quoteRequestResp + quotesIn []quoteInRequestResp + expectedOut []loopOutRequestResp + expectedIn []loopInRequestResp + keepDestAddr bool } // autoloop walks our test context through the process of triggering our @@ -269,6 +291,9 @@ func (c *autoloopTestCtx) autoloop(step *autoloopStep) { c.loopOuts <- step.existingOut c.loopIns <- step.existingIn + c.loopOutSingle = step.existingOutSingle + c.loopInSingle = step.existingInSingle + // Assert that we query the server for a quote for each of our // recommended swaps. Note that this differs from our set of expected // swaps because we may get quotes for suggested swaps but then just @@ -299,25 +324,77 @@ func (c *autoloopTestCtx) autoloop(step *autoloopStep) { c.quotes <- expected.quote } - // Assert that we dispatch the expected set of swaps. - for _, expected := range step.expectedOut { + require.True(c.t, c.matchLoopOuts(step.expectedOut, step.keepDestAddr)) + require.True(c.t, c.matchLoopIns(step.expectedIn)) +} + +// matchLoopOuts checks that the actual loop out requests we got match the +// expected ones. The argument keepDestAddr is used to indicate whether we keep +// the actual loops destination address for the comparison. This is useful +// because we don't want to compare the destination address generated by the +// wallet mock. We want to compare the destination address when testing the +// autoloop DestAddr parameter for loop outs. +func (c *autoloopTestCtx) matchLoopOuts(swaps []loopOutRequestResp, + keepDestAddr bool) bool { + + swapsCopy := make([]loopOutRequestResp, len(swaps)) + copy(swapsCopy, swaps) + + length := len(swapsCopy) + + for i := 0; i < length; i++ { actual := <-c.outRequest - // Set our destination address to nil so that we do not need to - // provide the address that is obtained by the mock wallet kit. - if expected.request.DestAddr == nil { + if !keepDestAddr { actual.DestAddr = nil } - assert.Equal(c.t, expected.request, actual) - c.loopOut <- expected.response + inner: + for index, swap := range swapsCopy { + equal := reflect.DeepEqual(swap.request, actual) + + if equal { + c.loopOut <- swap.response + + swapsCopy = append( + swapsCopy[:index], + swapsCopy[index+1:]..., + ) + + break inner + } + } } - for _, expected := range step.expectedIn { + return len(swapsCopy) == 0 +} + +// matchLoopIns checks that the actual loop in requests we got match the +// expected ones. +func (c *autoloopTestCtx) matchLoopIns( + swaps []loopInRequestResp) bool { + + swapsCopy := make([]loopInRequestResp, len(swaps)) + copy(swapsCopy, swaps) + + for i := 0; i < len(swapsCopy); i++ { actual := <-c.inRequest - assert.Equal(c.t, expected.request, actual) + inner: + for i, swap := range swapsCopy { + equal := reflect.DeepEqual(swap.request, actual) - c.loopIn <- expected.response + if equal { + c.loopIn <- swap.response + + swapsCopy = append( + swapsCopy[:i], swapsCopy[i+1:]..., + ) + + break inner + } + } } + + return len(swapsCopy) == 0 }