htlcswitch/switch test: add TestSwitchGetPaymentResult

TestSwitchGetPaymentResult tests that the switch interacts as expected
with the circuit map and network result store when looking up the result
of a payment ID. This is important for not to lose results under
concurrent lookup and receiving results.
This commit is contained in:
Johan T. Halseth 2019-06-07 16:42:26 +02:00
parent dd3abbc4ef
commit dd88015985
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 168 additions and 0 deletions

@ -916,3 +916,58 @@ func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte,
Spend: make(chan *chainntnfs.SpendDetail),
}, nil
}
type mockCircuitMap struct {
lookup chan *PaymentCircuit
}
var _ CircuitMap = (*mockCircuitMap)(nil)
func (m *mockCircuitMap) OpenCircuits(...Keystone) error {
return nil
}
func (m *mockCircuitMap) TrimOpenCircuits(chanID lnwire.ShortChannelID,
start uint64) error {
return nil
}
func (m *mockCircuitMap) DeleteCircuits(inKeys ...CircuitKey) error {
return nil
}
func (m *mockCircuitMap) CommitCircuits(
circuit ...*PaymentCircuit) (*CircuitFwdActions, error) {
return nil, nil
}
func (m *mockCircuitMap) CloseCircuit(outKey CircuitKey) (*PaymentCircuit,
error) {
return nil, nil
}
func (m *mockCircuitMap) FailCircuit(inKey CircuitKey) (*PaymentCircuit,
error) {
return nil, nil
}
func (m *mockCircuitMap) LookupCircuit(inKey CircuitKey) *PaymentCircuit {
return <-m.lookup
}
func (m *mockCircuitMap) LookupOpenCircuit(outKey CircuitKey) *PaymentCircuit {
return nil
}
func (m *mockCircuitMap) LookupByPaymentHash(hash [32]byte) []*PaymentCircuit {
return nil
}
func (m *mockCircuitMap) NumPending() int {
return 0
}
func (m *mockCircuitMap) NumOpen() int {
return 0
}

@ -14,6 +14,7 @@ import (
"github.com/btcsuite/fastsha256"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/ticker"
)
@ -2125,3 +2126,115 @@ func TestUpdateFailMalformedHTLCErrorConversion(t *testing.T) {
assertPaymentFailure(t)
})
}
// TestSwitchGetPaymentResult tests that the switch interacts as expected with
// the circuit map and network result store when looking up the result of a
// payment ID. This is important for not to lose results under concurrent
// lookup and receiving results.
func TestSwitchGetPaymentResult(t *testing.T) {
t.Parallel()
const paymentID = 123
var preimg lntypes.Preimage
preimg[0] = 3
s, err := initSwitchWithDB(testStartingHeight, nil)
if err != nil {
t.Fatalf("unable to init switch: %v", err)
}
if err := s.Start(); err != nil {
t.Fatalf("unable to start switch: %v", err)
}
defer s.Stop()
lookup := make(chan *PaymentCircuit, 1)
s.circuits = &mockCircuitMap{
lookup: lookup,
}
// If the payment circuit is not found in the circuit map, the payment
// result must be found in the store if available. Since we haven't
// added anything to the store yet, ErrPaymentIDNotFound should be
// returned.
lookup <- nil
_, err = s.GetPaymentResult(
paymentID, lntypes.Hash{}, newMockDeobfuscator(),
)
if err != ErrPaymentIDNotFound {
t.Fatalf("expected ErrPaymentIDNotFound, got %v", err)
}
// Next let the lookup find the circuit in the circuit map. It should
// subscribe to payment results, and return the result when available.
lookup <- &PaymentCircuit{}
resultChan, err := s.GetPaymentResult(
paymentID, lntypes.Hash{}, newMockDeobfuscator(),
)
if err != nil {
t.Fatalf("unable to get payment result: %v", err)
}
// Add the result to the store.
n := &networkResult{
msg: &lnwire.UpdateFulfillHTLC{
PaymentPreimage: preimg,
},
unencrypted: true,
isResolution: true,
}
err = s.networkResults.storeResult(paymentID, n)
if err != nil {
t.Fatalf("unable to store result: %v", err)
}
// The result should be availble.
select {
case res, ok := <-resultChan:
if !ok {
t.Fatalf("channel was closed")
}
if res.Error != nil {
t.Fatalf("got unexpected error result")
}
if res.Preimage != preimg {
t.Fatalf("expected preimg %v, got %v",
preimg, res.Preimage)
}
case <-time.After(1 * time.Second):
t.Fatalf("result not received")
}
// As a final test, try to get the result again. Now that is no longer
// in the circuit map, it should be immediately available from the
// store.
lookup <- nil
resultChan, err = s.GetPaymentResult(
paymentID, lntypes.Hash{}, newMockDeobfuscator(),
)
if err != nil {
t.Fatalf("unable to get payment result: %v", err)
}
select {
case res, ok := <-resultChan:
if !ok {
t.Fatalf("channel was closed")
}
if res.Error != nil {
t.Fatalf("got unexpected error result")
}
if res.Preimage != preimg {
t.Fatalf("expected preimg %v, got %v",
preimg, res.Preimage)
}
case <-time.After(1 * time.Second):
t.Fatalf("result not received")
}
}