@@ -32,10 +32,12 @@ type mockLnd struct {
3232 invoiceReq chan lndclient.InvoiceSubscriptionRequest
3333 paymentReq chan lntypes.Hash
3434
35- callErr error
36- errChan chan error
37- invoiceChan chan * lndclient.Invoice
38- paymentChans map [lntypes.Hash ]chan lndclient.PaymentStatus
35+ invoiceSubscriptionErr error
36+ trackPaymentErr error
37+ invoiceErrChan chan error
38+ paymentErrChan chan error
39+ invoiceChan chan * lndclient.Invoice
40+ paymentChans map [lntypes.Hash ]chan lndclient.PaymentStatus
3941}
4042
4143func newMockLnd () * mockLnd {
@@ -44,9 +46,10 @@ func newMockLnd() *mockLnd {
4446 invoiceReq : make (
4547 chan lndclient.InvoiceSubscriptionRequest , 10 ,
4648 ),
47- paymentReq : make (chan lntypes.Hash , 10 ),
48- errChan : make (chan error , 10 ),
49- invoiceChan : make (chan * lndclient.Invoice ),
49+ paymentReq : make (chan lntypes.Hash , 10 ),
50+ invoiceErrChan : make (chan error , 10 ),
51+ paymentErrChan : make (chan error , 10 ),
52+ invoiceChan : make (chan * lndclient.Invoice ),
5053 paymentChans : make (
5154 map [lntypes.Hash ]chan lndclient.PaymentStatus ,
5255 ),
@@ -144,28 +147,28 @@ func (m *mockLnd) SubscribeInvoices(_ context.Context,
144147 req lndclient.InvoiceSubscriptionRequest ) (<- chan * lndclient.Invoice ,
145148 <- chan error , error ) {
146149
147- if m .callErr != nil {
148- return nil , nil , m .callErr
150+ if m .invoiceSubscriptionErr != nil {
151+ return nil , nil , m .invoiceSubscriptionErr
149152 }
150153
151154 m .invoiceReq <- req
152155
153- return m .invoiceChan , m .errChan , nil
156+ return m .invoiceChan , m .invoiceErrChan , nil
154157}
155158
156159// TrackPayment picks up a previously started payment and returns a payment
157160// update stream and an error stream.
158161func (m * mockLnd ) TrackPayment (_ context.Context ,
159162 hash lntypes.Hash ) (chan lndclient.PaymentStatus , chan error , error ) {
160163
161- if m .callErr != nil {
162- return nil , nil , m .callErr
164+ if m .trackPaymentErr != nil {
165+ return nil , nil , m .trackPaymentErr
163166 }
164167
165168 m .paymentReq <- hash
166169 m .paymentChans [hash ] = make (chan lndclient.PaymentStatus , 1 )
167170
168- return m .paymentChans [hash ], m .errChan , nil
171+ return m .paymentChans [hash ], m .paymentErrChan , nil
169172}
170173
171174// TestAccountService tests that the account service can track payments and
@@ -181,15 +184,92 @@ func TestAccountService(t *testing.T) {
181184 validate func (t * testing.T , lnd * mockLnd ,
182185 s * InterceptorService )
183186 }{{
184- name : "startup err on tracking payment " ,
187+ name : "startup err on invoice subscription " ,
185188 setup : func (t * testing.T , lnd * mockLnd , s * InterceptorService ) {
186- lnd .callErr = testErr
189+ lnd .invoiceSubscriptionErr = testErr
187190 },
188191 startupErr : testErr .Error (),
189192 validate : func (t * testing.T , lnd * mockLnd ,
190193 s * InterceptorService ) {
191194
192195 lnd .assertNoInvoiceRequest (t )
196+ require .False (t , s .IsRunning ())
197+ },
198+ }, {
199+ name : "err on invoice update" ,
200+ setup : func (t * testing.T , lnd * mockLnd , s * InterceptorService ) {
201+ acct := & OffChainBalanceAccount {
202+ ID : testID ,
203+ Type : TypeInitialBalance ,
204+ CurrentBalance : 1234 ,
205+ Invoices : AccountInvoices {
206+ testHash : {},
207+ },
208+ }
209+
210+ err := s .store .UpdateAccount (acct )
211+ require .NoError (t , err )
212+ },
213+ validate : func (t * testing.T , lnd * mockLnd ,
214+ s * InterceptorService ) {
215+
216+ // Start by closing the store. This should cause an
217+ // error once we make an invoice update, as the service
218+ // will fail when persisting the invoice update.
219+ s .store .Close ()
220+
221+ // Ensure that the service was started successfully and
222+ // still running though, despite the closing of the
223+ // db store.
224+ require .True (t , s .IsRunning ())
225+
226+ // Now let's send the invoice update, which should fail.
227+ lnd .invoiceChan <- & lndclient.Invoice {
228+ AddIndex : 12 ,
229+ SettleIndex : 12 ,
230+ Hash : testHash ,
231+ AmountPaid : 777 ,
232+ State : invpkg .ContractSettled ,
233+ }
234+
235+ // Ensure that the service was eventually disabled.
236+ assertEventually (t , func () bool {
237+ isRunning := s .IsRunning ()
238+ return isRunning == false
239+ })
240+ lnd .assertMainErrContains (t , "database not open" )
241+ },
242+ }, {
243+ name : "err in invoice err channel" ,
244+ setup : func (t * testing.T , lnd * mockLnd , s * InterceptorService ) {
245+ acct := & OffChainBalanceAccount {
246+ ID : testID ,
247+ Type : TypeInitialBalance ,
248+ CurrentBalance : 1234 ,
249+ Invoices : AccountInvoices {
250+ testHash : {},
251+ },
252+ }
253+
254+ err := s .store .UpdateAccount (acct )
255+ require .NoError (t , err )
256+ },
257+ validate : func (t * testing.T , lnd * mockLnd ,
258+ s * InterceptorService ) {
259+ // Ensure that the service was started successfully.
260+ require .True (t , s .IsRunning ())
261+
262+ // Now let's send an error over the invoice error
263+ // channel. This should disable the service.
264+ lnd .invoiceErrChan <- testErr
265+
266+ // Ensure that the service was eventually disabled.
267+ assertEventually (t , func () bool {
268+ isRunning := s .IsRunning ()
269+ return isRunning == false
270+ })
271+
272+ lnd .assertMainErrContains (t , testErr .Error ())
193273 },
194274 }, {
195275 name : "goroutine err sent on main err chan" ,
@@ -207,7 +287,7 @@ func TestAccountService(t *testing.T) {
207287 err := s .store .UpdateAccount (acct )
208288 require .NoError (t , err )
209289
210- lnd .errChan <- testErr
290+ lnd .mainErrChan <- testErr
211291 },
212292 validate : func (t * testing.T , lnd * mockLnd ,
213293 s * InterceptorService ) {
@@ -239,6 +319,135 @@ func TestAccountService(t *testing.T) {
239319 lnd .assertNoPaymentRequest (t )
240320 lnd .assertInvoiceRequest (t , 0 , 0 )
241321 lnd .assertNoMainErr (t )
322+ require .True (t , s .IsRunning ())
323+ },
324+ }, {
325+ name : "startup err on payment tracking" ,
326+ setup : func (t * testing.T , lnd * mockLnd , s * InterceptorService ) {
327+ acct := & OffChainBalanceAccount {
328+ ID : testID ,
329+ Type : TypeInitialBalance ,
330+ CurrentBalance : 1234 ,
331+ Invoices : AccountInvoices {
332+ testHash : {},
333+ },
334+ Payments : AccountPayments {
335+ testHash : {
336+ Status : lnrpc .Payment_IN_FLIGHT ,
337+ FullAmount : 1234 ,
338+ },
339+ },
340+ }
341+
342+ err := s .store .UpdateAccount (acct )
343+ require .NoError (t , err )
344+
345+ lnd .trackPaymentErr = testErr
346+ },
347+ validate : func (t * testing.T , lnd * mockLnd ,
348+ s * InterceptorService ) {
349+
350+ // Assert that the invoice subscription succeeded.
351+ require .Contains (t , s .invoiceToAccount , testHash )
352+
353+ // But setting up the payment tracking should have failed.
354+ require .False (t , s .IsRunning ())
355+
356+ // Finally let's assert that we didn't successfully add the
357+ // payment to pending payment, and that lnd isn't awaiting
358+ // the payment request.
359+ require .NotContains (t , s .pendingPayments , testHash )
360+ lnd .assertNoPaymentRequest (t )
361+ },
362+ }, {
363+ name : "err on payment update" ,
364+ setup : func (t * testing.T , lnd * mockLnd , s * InterceptorService ) {
365+ acct := & OffChainBalanceAccount {
366+ ID : testID ,
367+ Type : TypeInitialBalance ,
368+ CurrentBalance : 1234 ,
369+ Payments : AccountPayments {
370+ testHash : {
371+ Status : lnrpc .Payment_IN_FLIGHT ,
372+ FullAmount : 1234 ,
373+ },
374+ },
375+ }
376+
377+ err := s .store .UpdateAccount (acct )
378+ require .NoError (t , err )
379+ },
380+ validate : func (t * testing.T , lnd * mockLnd ,
381+ s * InterceptorService ) {
382+ // Ensure that the service was started successfully,
383+ // and lnd contains the payment request.
384+ require .True (t , s .IsRunning ())
385+ lnd .assertPaymentRequests (t , map [lntypes.Hash ]struct {}{
386+ testHash : {},
387+ })
388+
389+ // Now let's wipe the service's pending payments.
390+ // This will cause an error send an update over
391+ // the payment channel, which should disable the
392+ // service.
393+ s .pendingPayments = make (map [lntypes.Hash ]* trackedPayment )
394+
395+ // Send an invalid payment over the payment chan
396+ // which should error and disable the service
397+ lnd .paymentChans [testHash ] <- lndclient.PaymentStatus {
398+ State : lnrpc .Payment_SUCCEEDED ,
399+ Fee : 234 ,
400+ Value : 1000 ,
401+ }
402+
403+ // Ensure that the service was eventually disabled.
404+ assertEventually (t , func () bool {
405+ isRunning := s .IsRunning ()
406+ return isRunning == false
407+ })
408+ lnd .assertMainErrContains (
409+ t , "not mapped to any account" ,
410+ )
411+
412+ },
413+ }, {
414+ name : "err in payment update chan" ,
415+ setup : func (t * testing.T , lnd * mockLnd , s * InterceptorService ) {
416+ acct := & OffChainBalanceAccount {
417+ ID : testID ,
418+ Type : TypeInitialBalance ,
419+ CurrentBalance : 1234 ,
420+ Payments : AccountPayments {
421+ testHash : {
422+ Status : lnrpc .Payment_IN_FLIGHT ,
423+ FullAmount : 1234 ,
424+ },
425+ },
426+ }
427+
428+ err := s .store .UpdateAccount (acct )
429+ require .NoError (t , err )
430+ },
431+ validate : func (t * testing.T , lnd * mockLnd ,
432+ s * InterceptorService ) {
433+ // Ensure that the service was started successfully,
434+ // and lnd contains the payment request.
435+ require .True (t , s .IsRunning ())
436+ lnd .assertPaymentRequests (t , map [lntypes.Hash ]struct {}{
437+ testHash : {},
438+ })
439+
440+ // Now let's send an error over the payment error
441+ // channel. This should disable the service.
442+ lnd .paymentErrChan <- testErr
443+
444+ // Ensure that the service was eventually disabled.
445+ assertEventually (t , func () bool {
446+ isRunning := s .IsRunning ()
447+ return isRunning == false
448+ })
449+
450+ lnd .assertMainErrContains (t , testErr .Error ())
242451 },
243452 }, {
244453 name : "startup track in-flight payments" ,
0 commit comments