mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 22:34:47 +00:00
* Feat(CoinStats): Scaffold implementation, not yet functional * Feat(CoinStats): Implement crypto wallet balance and transactions * Feat(CoinStats): Add tests, Minor improvements * Feat(CoinStats): Utilize bulk fetch API endpoints * Feat(CoinStats): Migrate strings to i8n * Feat(CoinStats): Fix error handling in wallet link modal * Feat(CoinStats): Implement hourly provider sync job * Feat(CoinStats): Generate docstrings * Fix(CoinStats): Validate API Key on provider update * Fix(Providers): Safely handle race condition in merchance creation * Fix(CoinStats): Don't catch system signals in account processor * Fix(CoinStats): Preload before iterating accounts * Fix(CoinStats): Add no opener / referrer to API dashboard link * Fix(CoinStats): Use strict matching for symbols * Fix(CoinStats): Remove dead code in transactions importer * Fix(CoinStats): Avoid transaction fallback ID collisions * Fix(CoinStats): Improve Blockchains fetch error handling * Fix(CoinStats): Enforce NOT NULL constraint for API Key schema * Fix(CoinStats): Migrate sync status strings to i8n * Fix(CoinStats): Use class name rather than hardcoded string * Fix(CoinStats): Use account currency rather than hardcoded USD * Fix(CoinStats): Migrate from standalone to Provider class * Fix(CoinStats): Fix test failures due to string changes
160 lines
4.6 KiB
Ruby
160 lines
4.6 KiB
Ruby
require "test_helper"
|
|
|
|
class CoinstatsAccount::ProcessorTest < ActiveSupport::TestCase
|
|
setup do
|
|
@family = families(:dylan_family)
|
|
@coinstats_item = CoinstatsItem.create!(
|
|
family: @family,
|
|
name: "Test CoinStats Connection",
|
|
api_key: "test_api_key_123"
|
|
)
|
|
@crypto = Crypto.create!
|
|
@account = @family.accounts.create!(
|
|
accountable: @crypto,
|
|
name: "Test Crypto Account",
|
|
balance: 1000,
|
|
currency: "USD"
|
|
)
|
|
@coinstats_account = @coinstats_item.coinstats_accounts.create!(
|
|
name: "Test Wallet",
|
|
currency: "USD",
|
|
current_balance: 2500
|
|
)
|
|
AccountProvider.create!(account: @account, provider: @coinstats_account)
|
|
end
|
|
|
|
test "skips processing when no linked account" do
|
|
# Create an unlinked coinstats account
|
|
unlinked_account = @coinstats_item.coinstats_accounts.create!(
|
|
name: "Unlinked Wallet",
|
|
currency: "USD",
|
|
current_balance: 1000
|
|
)
|
|
|
|
processor = CoinstatsAccount::Processor.new(unlinked_account)
|
|
|
|
# Should not raise, just return early
|
|
assert_nothing_raised do
|
|
processor.process
|
|
end
|
|
end
|
|
|
|
test "updates account balance from coinstats account" do
|
|
@coinstats_account.update!(current_balance: 5000.50)
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
processor.process
|
|
|
|
@account.reload
|
|
assert_equal BigDecimal("5000.50"), @account.balance
|
|
assert_equal BigDecimal("5000.50"), @account.cash_balance
|
|
end
|
|
|
|
test "updates account currency from coinstats account" do
|
|
@coinstats_account.update!(currency: "EUR")
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
processor.process
|
|
|
|
@account.reload
|
|
assert_equal "EUR", @account.currency
|
|
end
|
|
|
|
test "handles zero balance" do
|
|
@coinstats_account.update!(current_balance: 0)
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
processor.process
|
|
|
|
@account.reload
|
|
assert_equal BigDecimal("0"), @account.balance
|
|
end
|
|
|
|
test "handles nil balance as zero" do
|
|
@coinstats_account.update!(current_balance: nil)
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
processor.process
|
|
|
|
@account.reload
|
|
assert_equal BigDecimal("0"), @account.balance
|
|
end
|
|
|
|
test "processes transactions" do
|
|
@coinstats_account.update!(raw_transactions_payload: [
|
|
{
|
|
type: "Received",
|
|
date: "2025-01-15T10:00:00.000Z",
|
|
coinData: { count: 1.0, symbol: "ETH", currentValue: 2000 },
|
|
hash: { id: "0xabc123", explorerUrl: "https://etherscan.io/tx/0xabc123" }
|
|
}
|
|
])
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
|
|
# Mock the transaction processor to verify it's called
|
|
CoinstatsAccount::Transactions::Processor.any_instance
|
|
.expects(:process)
|
|
.returns({ success: true, total: 1, imported: 1, failed: 0, errors: [] })
|
|
.once
|
|
|
|
processor.process
|
|
end
|
|
|
|
test "continues processing when transaction processing fails" do
|
|
@coinstats_account.update!(raw_transactions_payload: [
|
|
{ type: "Received", date: "2025-01-15T10:00:00.000Z" }
|
|
])
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
|
|
# Mock transaction processing to raise an error
|
|
CoinstatsAccount::Transactions::Processor.any_instance
|
|
.expects(:process)
|
|
.raises(StandardError.new("Transaction processing error"))
|
|
|
|
# Should not raise - error is caught and reported
|
|
assert_nothing_raised do
|
|
processor.process
|
|
end
|
|
|
|
# Balance should still be updated
|
|
@account.reload
|
|
assert_equal BigDecimal("2500"), @account.balance
|
|
end
|
|
|
|
test "normalizes currency codes" do
|
|
@coinstats_account.update!(currency: "usd")
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
processor.process
|
|
|
|
@account.reload
|
|
assert_equal "USD", @account.currency
|
|
end
|
|
|
|
test "falls back to account currency when coinstats currency is nil" do
|
|
@account.update!(currency: "GBP")
|
|
# Use update_column to bypass validation
|
|
@coinstats_account.update_column(:currency, "")
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
processor.process
|
|
|
|
@account.reload
|
|
# Empty currency falls through to account's existing currency
|
|
assert_equal "GBP", @account.currency
|
|
end
|
|
|
|
test "raises error when account update fails" do
|
|
# Make the account invalid by directly modifying a validation constraint
|
|
Account.any_instance.stubs(:update!).raises(ActiveRecord::RecordInvalid.new(@account))
|
|
|
|
processor = CoinstatsAccount::Processor.new(@coinstats_account)
|
|
|
|
assert_raises(ActiveRecord::RecordInvalid) do
|
|
processor.process
|
|
end
|
|
end
|
|
end
|