Respect provider rate limits and preserve bank connections on throttling

This commit is contained in:
Lars Nolden
2026-09-10 17:25:09 +02:00
parent 2259db3e85
commit ba3ea6ae5a
14 changed files with 1250 additions and 95 deletions
+11 -4
View File
@@ -22,10 +22,10 @@ type backfillBank struct {
fetchErr error
}
func (b *backfillBank) Status(ctx context.Context, id string) (banking.Session, error) {
func (b *backfillBank) Status(ctx context.Context, id string) (banking.SessionStatus, error) {
b.statusIDs = append(b.statusIDs, id)
if b.statusErr != nil {
return banking.Session{}, b.statusErr
return banking.SessionStatus{}, b.statusErr
}
return b.historyBank.Status(ctx, id)
}
@@ -204,7 +204,7 @@ func TestBackfillRejectsUnsafePrerequisitesBeforeProviderContact(t *testing.T) {
}
func TestBackfillProviderFailuresDoNotImportPartialData(t *testing.T) {
for _, scenario := range []string{"not configured", "unavailable", "revoked", "provider expired", "account absent", "partial retrieval"} {
for _, scenario := range []string{"not configured", "unavailable", "revoked", "provider expired", "account absent", "partial retrieval", "status rate limit", "retrieval rate limit"} {
t.Run(scenario, func(t *testing.T) {
a, s, b := backfillApp(t)
s = seed(t, a, s)
@@ -221,6 +221,10 @@ func TestBackfillProviderFailuresDoNotImportPartialData(t *testing.T) {
b.session.Accounts = []domain.Account{{ID: s.Data.Accounts[0].ID, ExternalAccountID: "wrong_uid"}}
case "partial retrieval":
b.fetchErr = errors.New("private provider response")
case "status rate limit":
b.statusErr = bankRateError(t)
case "retrieval rate limit":
b.fetchErr = bankRateError(t)
}
beforeOps, err := json.Marshal(a.ops)
if err != nil {
@@ -233,7 +237,10 @@ func TestBackfillProviderFailuresDoNotImportPartialData(t *testing.T) {
if strings.Contains(err.Error(), "private provider response") {
t.Fatal("provider error exposed private response data")
}
if scenario != "partial retrieval" && len(b.accounts) != 0 {
if strings.Contains(scenario, "rate limit") && (!strings.Contains(err.Error(), "429") || !strings.Contains(err.Error(), "retry") || errors.Is(err, banking.ErrReconnect)) {
t.Fatal("backfill obscured rate limiting or falsely required reconnect")
}
if scenario != "partial retrieval" && scenario != "retrieval rate limit" && len(b.accounts) != 0 {
t.Fatal("unavailable consent reached transaction retrieval")
}
current, err := a.Snapshot(context.Background())