Files
sure/test/controllers/coinstats_items_controller_test.rb
Ethan 3b4ab735b0 Add (beta) CoinStats Crypto Wallet Integration with Balance and Transaction Syncing (#512)
* 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
2026-01-07 15:59:04 +01:00

179 lines
5.1 KiB
Ruby

require "test_helper"
class CoinstatsItemsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in users(:family_admin)
@family = families(:dylan_family)
@coinstats_item = CoinstatsItem.create!(
family: @family,
name: "Test CoinStats Connection",
api_key: "test_api_key_123"
)
end
# Helper to wrap data in Provider::Response
def success_response(data)
Provider::Response.new(success?: true, data: data, error: nil)
end
def error_response(message)
Provider::Response.new(success?: false, data: nil, error: Provider::Error.new(message))
end
test "should get new" do
get new_coinstats_item_url
assert_response :success
end
test "should create coinstats item with valid api key" do
# Mock the API key validation
Provider::Coinstats.any_instance.expects(:get_blockchains).returns(success_response([])).once
assert_difference("CoinstatsItem.count", 1) do
post coinstats_items_url, params: {
coinstats_item: {
name: "New CoinStats Connection",
api_key: "valid_api_key"
}
}
end
end
test "should not create coinstats item with invalid api key" do
# Mock the API key validation to fail
Provider::Coinstats.any_instance.expects(:get_blockchains)
.returns(error_response("Invalid API key"))
assert_no_difference("CoinstatsItem.count") do
post coinstats_items_url, params: {
coinstats_item: {
name: "New CoinStats Connection",
api_key: "invalid_api_key"
}
}
end
end
test "should destroy coinstats item" do
# Schedules for deletion, doesn't actually delete immediately
assert_no_difference("CoinstatsItem.count") do
delete coinstats_item_url(@coinstats_item)
end
assert_redirected_to settings_providers_path
@coinstats_item.reload
assert @coinstats_item.scheduled_for_deletion?
end
test "should sync coinstats item" do
post sync_coinstats_item_url(@coinstats_item)
assert_redirected_to accounts_path
end
test "sync responds to json format" do
post sync_coinstats_item_url(@coinstats_item, format: :json)
assert_response :ok
end
test "should update coinstats item with valid api key" do
Provider::Coinstats.any_instance.expects(:get_blockchains).returns(success_response([])).once
patch coinstats_item_url(@coinstats_item), params: {
coinstats_item: {
name: "Updated Name",
api_key: "new_valid_api_key"
}
}
@coinstats_item.reload
assert_equal "Updated Name", @coinstats_item.name
end
test "should not update coinstats item with invalid api key" do
Provider::Coinstats.any_instance.expects(:get_blockchains)
.returns(error_response("Invalid API key"))
original_name = @coinstats_item.name
patch coinstats_item_url(@coinstats_item), params: {
coinstats_item: {
name: "Updated Name",
api_key: "invalid_api_key"
}
}
@coinstats_item.reload
assert_equal original_name, @coinstats_item.name
end
test "link_wallet requires all parameters" do
post link_wallet_coinstats_items_url, params: {
coinstats_item_id: @coinstats_item.id,
address: "0x123"
# missing blockchain
}
assert_response :unprocessable_entity
end
test "link_wallet with valid params creates accounts" do
balance_data = [
{ coinId: "ethereum", name: "Ethereum", symbol: "ETH", amount: 1.5, price: 2000 }
]
bulk_response = [
{ blockchain: "ethereum", address: "0x123abc", connectionId: "ethereum", balances: balance_data }
]
Provider::Coinstats.any_instance.expects(:get_wallet_balances)
.with("ethereum:0x123abc")
.returns(success_response(bulk_response))
Provider::Coinstats.any_instance.expects(:extract_wallet_balance)
.with(bulk_response, "0x123abc", "ethereum")
.returns(balance_data)
assert_difference("Account.count", 1) do
assert_difference("CoinstatsAccount.count", 1) do
post link_wallet_coinstats_items_url, params: {
coinstats_item_id: @coinstats_item.id,
address: "0x123abc",
blockchain: "ethereum"
}
end
end
assert_redirected_to accounts_path
end
test "link_wallet handles provider errors" do
Provider::Coinstats.any_instance.expects(:get_wallet_balances)
.raises(Provider::Coinstats::Error.new("Invalid API key"))
post link_wallet_coinstats_items_url, params: {
coinstats_item_id: @coinstats_item.id,
address: "0x123abc",
blockchain: "ethereum"
}
assert_response :unprocessable_entity
end
test "link_wallet handles no tokens found" do
Provider::Coinstats.any_instance.expects(:get_wallet_balances)
.returns(success_response([]))
Provider::Coinstats.any_instance.expects(:extract_wallet_balance)
.returns([])
post link_wallet_coinstats_items_url, params: {
coinstats_item_id: @coinstats_item.id,
address: "0x123abc",
blockchain: "ethereum"
}
assert_response :unprocessable_entity
assert_match(/No tokens found/, response.body)
end
end