From a9d0f67b38173f2eb8195bb16d5a3040532e24d7 Mon Sep 17 00:00:00 2001 From: onin Date: Mon, 21 Sep 2026 14:17:00 +0200 Subject: [PATCH] fix: reset Hermes failure count after successful promise --- session/pingpong/invoice_tracker.go | 18 ++++++++- session/pingpong/invoice_tracker_test.go | 48 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/session/pingpong/invoice_tracker.go b/session/pingpong/invoice_tracker.go index 383f93f543..8e0e9e74eb 100644 --- a/session/pingpong/invoice_tracker.go +++ b/session/pingpong/invoice_tracker.go @@ -426,8 +426,24 @@ func (it *InvoiceTracker) WaitFirstInvoice(wait time.Duration) error { } func (it *InvoiceTracker) handlePromiseErrors(ch <-chan error) { + hadResult := false for err := range ch { - it.promiseErrors <- err + hadResult = true + select { + case it.promiseErrors <- err: + case <-it.stop: + return + } + } + + // HermesPromiseHandler closes the channel without sending a value when a + // promise request completes successfully. Propagate that completion so the + // tracker can reset its consecutive Hermes failure count. + if !hadResult { + select { + case it.promiseErrors <- nil: + case <-it.stop: + } } } diff --git a/session/pingpong/invoice_tracker_test.go b/session/pingpong/invoice_tracker_test.go index 55c6300c3e..35bd24d3ad 100644 --- a/session/pingpong/invoice_tracker_test.go +++ b/session/pingpong/invoice_tracker_test.go @@ -729,6 +729,54 @@ func TestInvoiceTracker_receiveExchangeMessageOrTimeout(t *testing.T) { } } +func TestInvoiceTracker_handlePromiseErrorsReportsSuccessfulCompletion(t *testing.T) { + it := &InvoiceTracker{ + stop: make(chan struct{}), + promiseErrors: make(chan error, 1), + } + + result := make(chan error) + close(result) + + it.handlePromiseErrors(result) + + assert.NoError(t, <-it.promiseErrors) +} + +func TestInvoiceTracker_handlePromiseErrorsDoesNotReportSuccessAfterError(t *testing.T) { + it := &InvoiceTracker{ + stop: make(chan struct{}), + promiseErrors: make(chan error, 2), + } + + result := make(chan error, 1) + result <- ErrHermesInternal + close(result) + + it.handlePromiseErrors(result) + + assert.ErrorIs(t, <-it.promiseErrors, ErrHermesInternal) + select { + case err := <-it.promiseErrors: + t.Fatalf("unexpected extra promise result: %v", err) + default: + } +} + +func TestInvoiceTracker_handleHermesErrorResetsFailureCountOnSuccess(t *testing.T) { + it := &InvoiceTracker{ + deps: InvoiceTrackerDeps{ + MaxHermesFailureCount: 10, + }, + } + + assert.NoError(t, it.handleHermesError(ErrHermesInternal)) + assert.Equal(t, uint64(1), it.hermesFailureCount) + + assert.NoError(t, it.handleHermesError(nil)) + assert.Zero(t, it.hermesFailureCount) +} + func TestInvoiceTracker_handleHermesError(t *testing.T) { tests := []struct { name string