Configure initial bank history in the connection UI
This commit is contained in:
@@ -11,12 +11,22 @@ import (
|
||||
|
||||
type historyBank struct {
|
||||
bankScenario
|
||||
fetched chan struct{}
|
||||
fetched chan struct{}
|
||||
authState string
|
||||
authorizations int
|
||||
fromDates []string
|
||||
}
|
||||
|
||||
func (b *historyBank) Authorize(_ context.Context, _, _, state string) (string, error) {
|
||||
b.authState = state
|
||||
b.authorizations++
|
||||
return "https://bank.example/authorize", nil
|
||||
}
|
||||
|
||||
func (b *historyBank) Transactions(_ context.Context, account domain.Account, from, to string) ([]domain.Facts, error) {
|
||||
b.fromDates = append(b.fromDates, from)
|
||||
var rows []domain.Facts
|
||||
for _, days := range []int{60, 1} {
|
||||
for _, days := range []int{400, 300, 1} {
|
||||
date := time.Now().UTC().AddDate(0, 0, -days).Format("2006-01-02")
|
||||
if date >= from && date <= to {
|
||||
rows = append(rows, domain.Facts{Source: "enablebanking", AccountID: account.ID, BookingDate: date, Amount: "-10.00", Currency: "EUR", RawDescription: "Card payment", ExternalID: "entry_" + date})
|
||||
@@ -40,10 +50,16 @@ func TestNewAccountImportsHistoryIndependentOfExistingSyncCursor(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
session := banking.Session{ID: "consent", ValidUntil: time.Now().Add(24 * time.Hour).Format(time.RFC3339), Accounts: s.Data.Accounts}
|
||||
a.bank = &historyBank{bankScenario: bankScenario{session: session}}
|
||||
b := &historyBank{bankScenario: bankScenario{session: session}}
|
||||
a.bank = b
|
||||
a.ops.Sessions = []banking.Session{session}
|
||||
a.ops.LastSync = time.Now().UTC().Add(-24 * time.Hour).Format(time.RFC3339)
|
||||
a.ops.AccountSync[old.ID] = a.ops.LastSync
|
||||
if err := a.saveOps(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a = reopenBankingApp(t, a)
|
||||
a.bank = b
|
||||
after, err := a.Sync(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -56,6 +72,67 @@ func TestNewAccountImportsHistoryIndependentOfExistingSyncCursor(t *testing.T) {
|
||||
t.Fatalf("new account history skipped: %v", counts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizedHistorySurvivesReopenAndRespectsIncrementalCursor(t *testing.T) {
|
||||
a, s := testApp(t)
|
||||
ctx := context.Background()
|
||||
account := s.Data.Accounts[0]
|
||||
account.ExternalAccountID = "history_uid"
|
||||
b := &historyBank{bankScenario: bankScenario{session: banking.Session{ID: "history_session", ValidUntil: time.Now().Add(24 * time.Hour).Format(time.RFC3339), Accounts: []domain.Account{account}}}}
|
||||
a.bank = b
|
||||
for _, months := range []int{-1, 0, 121} {
|
||||
if _, err := a.Authorize(ctx, "N26", "DE", months); err == nil {
|
||||
t.Fatalf("accepted invalid history choice %d", months)
|
||||
}
|
||||
}
|
||||
if b.authorizations != 0 {
|
||||
t.Fatal("invalid history choice reached the bank")
|
||||
}
|
||||
if _, err := a.Authorize(ctx, "N26", "DE", 24); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := a.Callback(ctx, "one_time_code", b.authState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a = reopenBankingApp(t, a)
|
||||
a.bank = b
|
||||
s, err := a.Snapshot(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(s.Connections) != 1 || s.Connections[0].HistoryMonths != 24 {
|
||||
t.Fatalf("saved history choice unavailable after restart: %+v", s.Connections)
|
||||
}
|
||||
before := time.Now().UTC().AddDate(0, -24, 0).Format("2006-01-02")
|
||||
first, err := a.Sync(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
after := time.Now().UTC().AddDate(0, -24, 0).Format("2006-01-02")
|
||||
if len(first.Data.Transactions) != 3 {
|
||||
t.Fatalf("selected history did not import older transactions: %+v", first.Data.Transactions)
|
||||
}
|
||||
if len(b.fromDates) != 1 || (b.fromDates[0] != before && b.fromDates[0] != after) {
|
||||
t.Fatalf("initial import did not use 24 calendar months: %v", b.fromDates)
|
||||
}
|
||||
cursor, err := time.Parse(time.RFC3339, first.Status.LastSync)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a = reopenBankingApp(t, a)
|
||||
a.bank = b
|
||||
again, err := a.Sync(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantFrom := cursor.AddDate(0, 0, -14).Format("2006-01-02")
|
||||
if len(b.fromDates) != 2 || b.fromDates[1] != wantFrom {
|
||||
t.Fatalf("incremental import ignored saved cursor: %v, want %s", b.fromDates, wantFrom)
|
||||
}
|
||||
if len(again.Data.Transactions) != 3 || again.Connections[0].HistoryMonths != 24 {
|
||||
t.Fatal("incremental import duplicated history or lost the selected window")
|
||||
}
|
||||
}
|
||||
func TestExpiredConsentIsVisibleBeforeNextScheduledSync(t *testing.T) {
|
||||
a, s := testApp(t)
|
||||
account := s.Data.Accounts[0]
|
||||
@@ -73,6 +150,9 @@ func TestExpiredConsentIsVisibleBeforeNextScheduledSync(t *testing.T) {
|
||||
if len(after.Connections) != 1 || after.Connections[0].Status != "reconnect_required" || after.Connections[0].Institution != "ING" {
|
||||
t.Fatalf("missing bank reconnect status: %+v", after.Connections)
|
||||
}
|
||||
if after.Connections[0].HistoryMonths != 12 {
|
||||
t.Fatal("legacy consent did not retain the default reconnect history")
|
||||
}
|
||||
}
|
||||
func TestRenewedConsentWakesSchedulerAndAutomaticallyImports(t *testing.T) {
|
||||
a, s := testApp(t)
|
||||
@@ -81,7 +161,7 @@ func TestRenewedConsentWakesSchedulerAndAutomaticallyImports(t *testing.T) {
|
||||
b := &historyBank{bankScenario: bankScenario{session: banking.Session{ID: "renewed_session", ValidUntil: time.Now().Add(24 * time.Hour).Format(time.RFC3339), Accounts: []domain.Account{account}}}, fetched: make(chan struct{}, 1)}
|
||||
a.bank = b
|
||||
a.ops.LastSync = time.Now().UTC().Format(time.RFC3339)
|
||||
a.authStates["state"] = authorization{Expires: time.Now().Add(time.Minute), Institution: "N26", Country: "DE"}
|
||||
a.authStates["state"] = authorization{Expires: time.Now().Add(time.Minute), Institution: "N26", Country: "DE", HistoryMonths: 12}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
done := make(chan struct{})
|
||||
go func() { defer close(done); a.RunScheduler(ctx) }()
|
||||
|
||||
Reference in New Issue
Block a user