Files
sure/test/models/provider/redbark_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

88 lines
3.2 KiB
Ruby

require "test_helper"
class Provider::RedbarkTest < ActiveSupport::TestCase
test "initializes with api key" do
provider = Provider::Redbark.new(api_key: "rbk_live_test")
assert_equal "rbk_live_test", provider.api_key
end
test "raises configuration error when api key blank" do
assert_raises(Provider::Redbark::ConfigurationError) do
Provider::Redbark.new(api_key: "")
end
end
test "error includes error_type" do
error = Provider::Redbark::Error.new("Test error", :unauthorized)
assert_equal "Test error", error.message
assert_equal :unauthorized, error.error_type
end
test "error defaults error_type to unknown" do
error = Provider::Redbark::Error.new("Test error")
assert_equal :unknown, error.error_type
end
test "rate limit and server errors are typed for retry handling" do
assert Provider::Redbark::RateLimitError.new("limited", :rate_limited).is_a?(Provider::Redbark::Error)
assert Provider::Redbark::ServerError.new("boom", :server_error).is_a?(Provider::Redbark::Error)
end
test "get_transactions splits the window when the server truncates" do
provider = Provider::Redbark.new(api_key: "rbk_live_test")
stub_request(:get, "https://api.redbark.com/v1/transactions")
.with(query: hash_including("from" => "2026-01-01", "to" => "2026-01-31"))
.to_return(
status: 200,
headers: { "Content-Type" => "application/json", "X-Redbark-Truncated" => "true" },
body: { data: [ { id: "t1" } ], pagination: { hasMore: true } }.to_json
)
stub_request(:get, "https://api.redbark.com/v1/transactions")
.with(query: hash_including("from" => "2026-01-01", "to" => "2026-01-16"))
.to_return(
status: 200,
headers: { "Content-Type" => "application/json" },
body: { data: [ { id: "t1" }, { id: "t2" } ], pagination: { hasMore: false } }.to_json
)
stub_request(:get, "https://api.redbark.com/v1/transactions")
.with(query: hash_including("from" => "2026-01-17", "to" => "2026-01-31"))
.to_return(
status: 200,
headers: { "Content-Type" => "application/json" },
body: { data: [ { id: "t3" } ], pagination: { hasMore: false } }.to_json
)
results = provider.get_transactions(
connection_id: "conn_1",
account_id: "acc_1",
start_date: Date.new(2026, 1, 1),
end_date: Date.new(2026, 1, 31)
)
assert_equal %w[t1 t2 t3], results.map { |t| t[:id] }
end
test "get_transactions raises when a truncated window cannot be narrowed" do
provider = Provider::Redbark.new(api_key: "rbk_live_test")
stub_request(:get, "https://api.redbark.com/v1/transactions")
.with(query: hash_including("connectionId" => "conn_1"))
.to_return(
status: 200,
headers: { "Content-Type" => "application/json", "X-Redbark-Truncated" => "true" },
body: { data: [ { id: "t1" } ], pagination: { hasMore: true } }.to_json
)
error = assert_raises(Provider::Redbark::Error) do
provider.get_transactions(
connection_id: "conn_1",
account_id: "acc_1",
start_date: Date.new(2026, 1, 5),
end_date: Date.new(2026, 1, 5)
)
end
assert_equal :truncated, error.error_type
end
end