mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Final deposit states should not stall while deposit locks are held, because a blocked manager receive loop can otherwise hold up the deposit FSM; if shutdown happens before notification delivery, startup recovery can still resume from the final state. Send finalization notifications from a goroutine so final states are recorded without waiting on the manager receive loop, and add tests for blocked manager delivery and shutdown races.
135 lines
3.3 KiB
Go
135 lines
3.3 KiB
Go
package deposit
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightninglabs/loop/fsm"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestFinalizeDepositActionDoesNotBlock ensures the final cleanup notification
|
|
// does not block the withdrawal completion path while the manager loop is busy.
|
|
func TestFinalizeDepositActionDoesNotBlock(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
|
|
outpoint := wire.OutPoint{
|
|
Hash: chainhash.Hash{1},
|
|
Index: 1,
|
|
}
|
|
|
|
depositFSM := &FSM{
|
|
deposit: &Deposit{
|
|
OutPoint: outpoint,
|
|
},
|
|
quitChan: make(chan struct{}),
|
|
finalizedDepositChan: make(chan wire.OutPoint),
|
|
}
|
|
|
|
resultChan := make(chan fsm.EventType, 1)
|
|
go func() {
|
|
resultChan <- depositFSM.FinalizeDepositAction(ctx, nil)
|
|
}()
|
|
|
|
select {
|
|
case result := <-resultChan:
|
|
require.Equal(t, fsm.NoOp, result)
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
t.Fatal("FinalizeDepositAction blocked on manager cleanup")
|
|
}
|
|
|
|
select {
|
|
case gotOutpoint := <-depositFSM.finalizedDepositChan:
|
|
require.Equal(t, outpoint, gotOutpoint)
|
|
|
|
case <-time.After(time.Second):
|
|
t.Fatal("finalization cleanup notification was not delivered")
|
|
}
|
|
}
|
|
|
|
// TestFinalizeDepositActionIgnoresRequestCancellation ensures the cleanup
|
|
// notification is tied to the FSM lifetime, not the caller's request context.
|
|
func TestFinalizeDepositActionIgnoresRequestCancellation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
quitChan := make(chan struct{})
|
|
defer close(quitChan)
|
|
|
|
outpoint := wire.OutPoint{
|
|
Hash: chainhash.Hash{2},
|
|
Index: 2,
|
|
}
|
|
|
|
depositFSM := &FSM{
|
|
deposit: &Deposit{
|
|
OutPoint: outpoint,
|
|
},
|
|
quitChan: quitChan,
|
|
finalizedDepositChan: make(chan wire.OutPoint),
|
|
}
|
|
|
|
resultChan := make(chan fsm.EventType, 1)
|
|
go func() {
|
|
resultChan <- depositFSM.FinalizeDepositAction(ctx, nil)
|
|
}()
|
|
|
|
select {
|
|
case result := <-resultChan:
|
|
require.Equal(t, fsm.NoOp, result)
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
t.Fatal("FinalizeDepositAction blocked on manager cleanup")
|
|
}
|
|
|
|
cancel()
|
|
|
|
select {
|
|
case gotOutpoint := <-depositFSM.finalizedDepositChan:
|
|
require.Equal(t, outpoint, gotOutpoint)
|
|
|
|
case <-time.After(time.Second):
|
|
t.Fatal("finalization cleanup notification was dropped after " +
|
|
"request cancellation")
|
|
}
|
|
}
|
|
|
|
// TestFinalizeDepositActionIgnoresCanceledContext ensures the final cleanup
|
|
// notification is still queued even if the caller's context is already done.
|
|
func TestFinalizeDepositActionIgnoresCanceledContext(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
quitChan := make(chan struct{})
|
|
defer close(quitChan)
|
|
|
|
outpoint := wire.OutPoint{
|
|
Hash: chainhash.Hash{3},
|
|
Index: 3,
|
|
}
|
|
|
|
depositFSM := &FSM{
|
|
deposit: &Deposit{
|
|
OutPoint: outpoint,
|
|
},
|
|
quitChan: quitChan,
|
|
finalizedDepositChan: make(chan wire.OutPoint),
|
|
}
|
|
|
|
result := depositFSM.FinalizeDepositAction(ctx, nil)
|
|
require.Equal(t, fsm.NoOp, result)
|
|
|
|
select {
|
|
case gotOutpoint := <-depositFSM.finalizedDepositChan:
|
|
require.Equal(t, outpoint, gotOutpoint)
|
|
|
|
case <-time.After(time.Second):
|
|
t.Fatal("finalization cleanup notification was dropped for " +
|
|
"an already-canceled request context")
|
|
}
|
|
}
|