mirror of
https://github.com/we-promise/sure.git
synced 2026-07-20 00:35:22 +00:00
* 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
99 lines
3.3 KiB
Ruby
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
|