mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 23:39:03 +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
This commit is contained in:
@@ -6,6 +6,58 @@ class PlaidItemsControllerTest < ActionDispatch::IntegrationTest
|
||||
sign_in @user = users(:family_admin)
|
||||
end
|
||||
|
||||
test "new redirects with friendly alert when Plaid rejects link_token request for unauthorized products" do
|
||||
# Reproduces issue #1792: the Plaid client account isn't enabled for the
|
||||
# requested products, so Plaid returns an actionable error message. We
|
||||
# should surface that message instead of letting the modal frame render
|
||||
# blank.
|
||||
plaid_provider = mock
|
||||
Provider::Registry.stubs(:plaid_provider_for_region).with(:us).returns(plaid_provider)
|
||||
|
||||
error_body = {
|
||||
"error_code" => "INVALID_PRODUCT",
|
||||
"error_message" => "Your account is not enabled for the following products: [\"investments\" \"liabilities\" \"transactions\"]. To request access, visit https://dashboard.plaid.com/overview/request-products or contact Sales or your Account Manager."
|
||||
}.to_json
|
||||
plaid_provider.expects(:get_link_token).raises(
|
||||
Plaid::ApiError.new(code: 400, response_body: error_body)
|
||||
)
|
||||
|
||||
get new_plaid_item_url(accountable_type: "Investment")
|
||||
|
||||
assert_redirected_to accounts_path
|
||||
assert_match(/not enabled for the following products/, flash[:alert])
|
||||
end
|
||||
|
||||
test "new redirects with generic alert when Plaid raises an unclassified error" do
|
||||
plaid_provider = mock
|
||||
Provider::Registry.stubs(:plaid_provider_for_region).with(:us).returns(plaid_provider)
|
||||
|
||||
plaid_provider.expects(:get_link_token).raises(
|
||||
Plaid::ApiError.new(code: 500, response_body: { "error_code" => "INTERNAL_SERVER_ERROR" }.to_json)
|
||||
)
|
||||
|
||||
get new_plaid_item_url
|
||||
|
||||
assert_redirected_to accounts_path
|
||||
assert_equal I18n.t("plaid_items.errors.link_token_generic"), flash[:alert]
|
||||
end
|
||||
|
||||
test "edit redirects with friendly alert when Plaid rejects update link_token request" do
|
||||
plaid_item = plaid_items(:one)
|
||||
error_body = {
|
||||
"error_code" => "INVALID_PRODUCT",
|
||||
"error_message" => "Your account is not enabled for the following products: [\"transactions\"]."
|
||||
}.to_json
|
||||
PlaidItem.any_instance.expects(:get_update_link_token).raises(
|
||||
Plaid::ApiError.new(code: 400, response_body: error_body)
|
||||
)
|
||||
|
||||
get edit_plaid_item_url(plaid_item)
|
||||
|
||||
assert_redirected_to accounts_path
|
||||
assert_match(/not enabled for the following products/, flash[:alert])
|
||||
end
|
||||
|
||||
test "create" do
|
||||
@plaid_provider = mock
|
||||
Provider::Registry.expects(:plaid_provider_for_region).with("us").returns(@plaid_provider)
|
||||
|
||||
@@ -44,4 +44,46 @@ class PlaidItemTest < ActiveSupport::TestCase
|
||||
@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
|
||||
|
||||
Reference in New Issue
Block a user