Files
sure/test/models/mercury_account/processor_test.rb
ghost 6768d03b3c feat(mercury): pending transactions, kind/counterpartyId metadata, and test coverage (#2452)
* feat(mercury): pending transactions, kind/counterpartyId metadata, and test coverage

- Transaction::PENDING_PROVIDERS: add "mercury" so the existing pending
  reconciliation pipeline (pending→posted amount matching in
  ProviderImportAdapter) activates for Mercury entries

- MercuryEntry::Processor: pass extra: to import_transaction with
    extra["mercury"]["pending"]        = true/false (status == "pending")
    extra["mercury"]["kind"]           = ACH / Wire / Card / etc.
    extra["mercury"]["counterparty_id"] = Mercury counterparty UUID
  Pending transactions are now imported with the flag set rather than
  being silently ignored; failed transactions continue to be skipped

- Tests (new files, 30 cases):
  - test/models/mercury_entry/processor_test.rb  — sign convention,
    date fallback, name priority, notes concat, pending flag, kind,
    counterpartyId, failed skip, idempotency, merchant creation,
    no-linked-account guard
  - test/models/mercury_item/importer_test.rb    — account discovery,
    no-duplicate unlinked records, balance update on linked accounts,
    transaction dedup (append-only new ids), sync window (90-day first
    sync, last_synced_at-7d subsequent), 401 marks requires_update
  - test/models/mercury_account/processor_test.rb — balance update,
    CreditCard sign negation, cash_balance parity, no-linked-account
    no-op, transaction processing delegation

* fix(mercury): address review findings — pending SQL, dedup upsert, N+1, nil assertion

- provider_import_adapter: add mercury to all three find_pending_transaction*
  SQL predicates so Mercury pending entries are found and claimed when the
  posted version arrives (exact, fuzzy, and low-confidence paths)

- mercury_item/importer: replace append-only dedup with an upsert-by-id
  that replaces the stored raw payload when status changes from pending to
  non-pending; prevents pending flag from persisting indefinitely when Mercury
  reuses the same transaction ID for the posted version

- kraken_account/ledger_processor: preload all existing kraken ledger
  external_ids into a Set before the loop; replaces per-entry exists? query
  (N+1) with an in-memory Set#include? lookup

- test/models/mercury_account/processor_test: capture return value from
  process and add assert_nil to enforce the nil contract stated in the test name

* fix(mercury): route transaction-count diagnostics through DebugLogEntry
2026-06-30 07:37:14 +02:00

99 lines
3.3 KiB
Ruby

# frozen_string_literal: true
require "test_helper"
class MercuryAccount::ProcessorTest < ActiveSupport::TestCase
setup do
@family = families(:dylan_family)
@item = MercuryItem.create!(family: @family, name: "Mercury", token: "tok")
end
# ---------------------------------------------------------------------------
# balance update
# ---------------------------------------------------------------------------
test "updates account balance from mercury_account current_balance" do
account = create_account("Checking")
mercury_account = create_mercury_account("acc_001", balance: 12_345.67, account: account)
MercuryAccount::Processor.new(mercury_account).process
assert_in_delta 12_345.67, account.reload.balance, 0.01
end
test "negates balance for CreditCard accounts" do
account = @family.accounts.create!(
name: "Mercury Credit", balance: 0, currency: "USD",
accountable: CreditCard.new
)
mercury_account = create_mercury_account("acc_credit", balance: 500.0, account: account)
MercuryAccount::Processor.new(mercury_account).process
assert_in_delta(-500.0, account.reload.balance, 0.01)
end
test "sets cash_balance equal to balance for depository" do
account = create_account("Savings")
mercury_account = create_mercury_account("acc_002", balance: 3_000.0, account: account)
MercuryAccount::Processor.new(mercury_account).process
assert_in_delta 3_000.0, account.reload.cash_balance, 0.01
end
# ---------------------------------------------------------------------------
# no linked account
# ---------------------------------------------------------------------------
test "returns nil without error when no linked account" do
mercury_account = @item.mercury_accounts.create!(
name: "Unlinked", account_id: "acc_unlinked", currency: "USD", current_balance: 100
)
result = nil
assert_nothing_raised do
result = MercuryAccount::Processor.new(mercury_account).process
end
assert_nil result
end
# ---------------------------------------------------------------------------
# transaction processing delegation
# ---------------------------------------------------------------------------
test "processes transactions stored in raw_transactions_payload" do
account = create_account("Checking")
mercury_account = create_mercury_account("acc_003", balance: 0, account: account,
raw_transactions: [
{ "id" => "tx_a", "amount" => 100.0, "status" => "sent",
"bankDescription" => "Deposit", "createdAt" => "2024-06-01T00:00:00Z",
"postedAt" => "2024-06-01T00:00:00Z" }
]
)
assert_difference "account.entries.count", 1 do
MercuryAccount::Processor.new(mercury_account).process
end
end
private
def create_account(name)
@family.accounts.create!(
name: name, balance: 0, currency: "USD",
accountable: Depository.new(subtype: "checking")
)
end
def create_mercury_account(account_id, balance:, account:, raw_transactions: [])
ma = @item.mercury_accounts.create!(
name: account_id, account_id: account_id, currency: "USD",
current_balance: balance,
raw_transactions_payload: raw_transactions
)
AccountProvider.create!(provider: ma, account: account)
ma
end
end