Files
sure/test/controllers/redbark_items_controller_test.rb
Oscar c6a240a183 feat(redbark): add australian bank sync (redbark) (#2794)
* add redbark provider integration

- per family api key provider, built like the lunchflow integration
- syncs accounts, balances and transactions from api.redbark.com
- account setup flow, settings panel, locales and routes
- tests and fixtures

* harden redbark integration based on prior provider pr feedback

- use DebugLogEntry.capture for sync/import/unlink failures
- retry 429s and 5xxs with backoff, raise on page cap instead of truncating
- keep raw response bodies out of logs and errors
- not null constraints on account columns, migration base 7.2
- persist ignored flag for skipped accounts so they stop nagging setup
- validate api key on every save, re-arm status on key rotation
- destroy aborts if unlink fails, atomic account create and link
- require_admin on mutating actions, see_other on error redirects
- single grouped query for item account counts
- i18n default connection name, blank password field value
- controller and provider tests

* fix issues found in second review sweep

- add missing syncable scope, without it every family sync raises
- kick off a sync on connection create and on key rotation
- setup dialog fetches accounts inline for fresh connections and shows api errors
- skip balance write when no balance has been fetched yet, never anchor a false zero
- exclude stale and non banking accounts from the batched balances call, per account fallback if the batch is rejected
- detect the server row ceiling and empty pages instead of silently truncating history
- user sync start date only governs the initial backfill, incremental after that
- fetch connections before the per account loop so auth errors propagate once
- drop untemplated index/show/new/edit routes and dead preload/link_accounts actions
- stable dom id on the settings panel so repeat turbo replaces keep working

* skip brokerage connections, found in live testing

- the transactions endpoint 400s for brokerage connections, they belong to /v1/trades
- only import accounts from banking and documents connections
- guard transaction fetches for any legacy linked non banking account

* address review feedback

- treat the truncation header as a pagination signal: split the date window and refetch instead of failing the account
- prune stale pending rows from the snapshot so settled pendings cant come back as duplicates
- block linking a sure account that already has another provider feed
- count setup failures separately from skips and surface an error instead of "all skipped"
- add not nulls on redbark_items name and api key
- enqueue the destroy job after the flag commits, not inside the transaction
- swap bg-gray-400 for bg-surface-inset, drop amounts from info logs, remove i18n default fallbacks
- tests for window splitting, pending pruning and encrypted payload round trip

* fix issues from convention review

- benign skips (unlinked account, blank id, unparseable rows) no longer count as failures, tracked separately so a clean batch reports success
- currency parsing goes through extract_currency so hash shaped payloads resolve instead of falling to the default
- merchant ids use truncated sha256 instead of md5
- debug log entries for import failures and account sync scheduling failures

* bound the raw transactions snapshot to the fetch window

- trim raw_transactions_payload to the current fetch window on merge, same as brex
- keep rows without a parseable date, drop settled pendings as before
- surface skipped rows in the aggregate debug log entry with imported/skipped counts
2026-07-26 07:40:25 +02:00

98 lines
2.8 KiB
Ruby

# frozen_string_literal: true
require "test_helper"
class RedbarkItemsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in users(:family_admin)
SyncJob.stubs(:perform_later)
@family = families(:dylan_family)
@redbark_item = redbark_items(:one)
@redbark_account = redbark_accounts(:savings_account)
end
test "create adds a new redbark connection" do
family = users(:empty).family
sign_in users(:empty)
assert_difference "RedbarkItem.count", 1 do
post redbark_items_url, params: {
redbark_item: { api_key: "rbk_live_new_key" }
}
end
item = family.redbark_items.order(:created_at).last
assert_equal "rbk_live_new_key", item.api_key
end
test "create with blank api key is rejected" do
assert_no_difference "RedbarkItem.count" do
post redbark_items_url, params: {
redbark_item: { api_key: "" }
}
end
end
test "update rotates the api key and clears requires_update" do
@redbark_item.update!(status: :requires_update)
patch redbark_item_url(@redbark_item), params: {
redbark_item: { api_key: "rbk_live_rotated" }
}
@redbark_item.reload
assert_equal "rbk_live_rotated", @redbark_item.api_key
assert @redbark_item.good?
end
test "blank api key update is rejected and preserves the stored key" do
original_key = @redbark_item.api_key
patch redbark_item_url(@redbark_item), params: {
redbark_item: { api_key: "" }
}
assert_equal original_key, @redbark_item.reload.api_key
end
test "sync enqueues a sync" do
RedbarkItem.any_instance.stubs(:syncing?).returns(false)
RedbarkItem.any_instance.expects(:sync_later).once
post sync_redbark_item_url(@redbark_item)
assert_response :redirect
end
test "destroy schedules the connection for deletion" do
delete redbark_item_url(@redbark_item)
assert_redirected_to settings_providers_path
assert @redbark_item.reload.scheduled_for_deletion?
end
test "complete_account_setup creates and links an account" do
assert_difference "Account.count", 1 do
post complete_account_setup_redbark_item_url(@redbark_item), params: {
accounts: { @redbark_account.id => { account_type: "depository" } }
}
end
@redbark_account.reload
assert @redbark_account.account_provider.present?
assert_equal "Depository", @redbark_account.account.accountable_type
assert_not @redbark_account.ignored?
end
test "complete_account_setup skip marks the account ignored" do
assert_no_difference "Account.count" do
post complete_account_setup_redbark_item_url(@redbark_item), params: {
accounts: { @redbark_account.id => { account_type: "skip" } }
}
end
assert @redbark_account.reload.ignored?
assert_not @redbark_item.reload.unlinked_redbark_accounts.exists?(id: @redbark_account.id)
end
end