mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 15:34:58 +00:00
* fix(plaid): surface configuration/product-access errors from Link flow (#1792) * fix(plaid): harden Plaid Link onExit guard + nil-body JSON parse (#1792 review) * fix lint check issue * fix test unit check
90 lines
3.2 KiB
Ruby
90 lines
3.2 KiB
Ruby
require "test_helper"
|
|
|
|
class PlaidItemTest < ActiveSupport::TestCase
|
|
include SyncableInterfaceTest
|
|
|
|
setup do
|
|
@plaid_item = @syncable = plaid_items(:one)
|
|
@plaid_provider = mock
|
|
Provider::Registry.stubs(:plaid_provider_for_region).returns(@plaid_provider)
|
|
end
|
|
|
|
test "removes plaid item when destroyed" do
|
|
@plaid_provider.expects(:remove_item).with(@plaid_item.access_token).once
|
|
|
|
assert_difference "PlaidItem.count", -1 do
|
|
@plaid_item.destroy
|
|
end
|
|
end
|
|
|
|
test "destroys item even when Plaid credentials are invalid" do
|
|
error_response = {
|
|
"error_code" => "INVALID_API_KEYS",
|
|
"error_message" => "invalid client_id or secret provided"
|
|
}.to_json
|
|
|
|
plaid_error = Plaid::ApiError.new(code: 400, response_body: error_response)
|
|
@plaid_provider.expects(:remove_item).raises(plaid_error)
|
|
|
|
assert_difference "PlaidItem.count", -1 do
|
|
@plaid_item.destroy
|
|
end
|
|
end
|
|
|
|
test "destroys item even when Plaid item not found" do
|
|
error_response = {
|
|
"error_code" => "ITEM_NOT_FOUND",
|
|
"error_message" => "item not found"
|
|
}.to_json
|
|
|
|
plaid_error = Plaid::ApiError.new(code: 400, response_body: error_response)
|
|
@plaid_provider.expects(:remove_item).raises(plaid_error)
|
|
|
|
assert_difference "PlaidItem.count", -1 do
|
|
@plaid_item.destroy
|
|
end
|
|
end
|
|
|
|
test "get_update_link_token marks item as requires_update and returns nil on ITEM_NOT_FOUND" do
|
|
error_response = { "error_code" => "ITEM_NOT_FOUND", "error_message" => "not found" }.to_json
|
|
Family.any_instance.expects(:get_link_token).raises(
|
|
Plaid::ApiError.new(code: 400, response_body: error_response)
|
|
)
|
|
|
|
result = @plaid_item.get_update_link_token(webhooks_url: "https://x", redirect_url: "https://x")
|
|
|
|
assert_nil result
|
|
assert_predicate @plaid_item.reload, :requires_update?
|
|
end
|
|
|
|
test "get_update_link_token re-raises other Plaid errors so the controller can surface them" do
|
|
# Issue #1792: silently swallowing all Plaid errors here is what made the
|
|
# "modal closes with nothing happening" experience so opaque.
|
|
error_response = { "error_code" => "INVALID_PRODUCT", "error_message" => "Your account is not enabled..." }.to_json
|
|
Family.any_instance.expects(:get_link_token).raises(
|
|
Plaid::ApiError.new(code: 400, response_body: error_response)
|
|
)
|
|
|
|
assert_raises(Plaid::ApiError) do
|
|
@plaid_item.get_update_link_token(webhooks_url: "https://x", redirect_url: "https://x")
|
|
end
|
|
assert_predicate @plaid_item.reload, :good?
|
|
end
|
|
|
|
test "get_update_link_token tolerates a Plaid::ApiError with a nil/blank response_body" do
|
|
# Plaid clients have been observed raising ApiError without a response
|
|
# body (network-layer failures, early aborts). The old JSON.parse would
|
|
# blow up with TypeError before the rescue could fire; we now coerce
|
|
# to String so the parse falls back to {} and the error re-raises
|
|
# cleanly for the controller to handle.
|
|
Family.any_instance.expects(:get_link_token).raises(
|
|
Plaid::ApiError.new(code: 500, response_body: nil)
|
|
)
|
|
|
|
assert_raises(Plaid::ApiError) do
|
|
@plaid_item.get_update_link_token(webhooks_url: "https://x", redirect_url: "https://x")
|
|
end
|
|
assert_predicate @plaid_item.reload, :good?
|
|
end
|
|
end
|