mirror of
https://github.com/we-promise/sure.git
synced 2026-05-09 21:54:58 +00:00
* Complete Sophtron account mapping * Clarify Sophtron login challenge flow * Add Sophtron connection UI timeout * Treat Sophtron timeout jobs as failed * Reset failed Sophtron connection state * Handle stale Sophtron connection jobs * Advance Sophtron polling timeout * Shorten Sophtron connection timeout * Fix Sophtron modal polling updates * Stabilize Sophtron MFA polling * Give Sophtron OTP challenges more time * Clarify Sophtron institution login failures * Extend Sophtron polling during login progress * Probe Sophtron accounts after completed MFA step * Align Sophtron dialogs with design system * Start Sophtron initial load after linking accounts * Fix Sophtron initial transaction load * Fail Sophtron sync without institution connection * Fix tests * Wrap Sophtron account linking in transaction * Wrap Sophtron provider responses * Fix Sophtron MFA security tests * Guard Sophtron MFA challenge arrays * Respect Sophtron initial load window * Use unique Sophtron MFA answer field ids * Address Sophtron review follow-ups * Fix Sophtron transaction sync refresh * Avoid blocking Sophtron refresh polling * Move Sophtron account helpers to model * Keep Sophtron grouping provider-level * Start new Sophtron institution links * Isolate Sophtron institution connections --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
606 lines
22 KiB
Ruby
606 lines
22 KiB
Ruby
require "test_helper"
|
|
|
|
class SophtronItemsControllerTest < ActionDispatch::IntegrationTest
|
|
setup do
|
|
sign_in @user = users(:family_admin)
|
|
@item = @user.family.sophtron_items.create!(
|
|
name: "Sophtron",
|
|
user_id: "developer-user",
|
|
access_key: Base64.strict_encode64("secret-key"),
|
|
customer_id: "cust-1"
|
|
)
|
|
end
|
|
|
|
test "select_accounts renders institution connection flow when no institution is connected" do
|
|
SophtronItem.any_instance.stubs(:ensure_customer!).returns("cust-1")
|
|
|
|
get select_accounts_sophtron_items_url
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Connect Sophtron Institution"
|
|
end
|
|
|
|
test "select_accounts renders institution search after failed connection attempt" do
|
|
@item.update!(user_institution_id: "ui-1", status: :requires_update)
|
|
SophtronItem.any_instance.stubs(:ensure_customer!).returns("cust-1")
|
|
|
|
get select_accounts_sophtron_items_url
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Connect Sophtron Institution"
|
|
end
|
|
|
|
test "select_accounts renders institution search after stale Sophtron timeout" do
|
|
@item.update!(
|
|
user_institution_id: "ui-1",
|
|
status: :good,
|
|
job_status: "Timeout",
|
|
raw_job_payload: {
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
SuccessFlag: false,
|
|
LastStep: "LogInPanel",
|
|
LastStatus: "Timeout"
|
|
}
|
|
)
|
|
SophtronItem.any_instance.stubs(:ensure_customer!).returns("cust-1")
|
|
|
|
get select_accounts_sophtron_items_url
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Connect Sophtron Institution"
|
|
end
|
|
|
|
test "select_accounts can start a new institution connection when already connected" do
|
|
@item.update!(user_institution_id: "ui-1", status: :good)
|
|
SophtronItem.any_instance.stubs(:ensure_customer!).returns("cust-1")
|
|
|
|
get select_accounts_sophtron_items_url(connect_new_institution: true)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Connect Sophtron Institution"
|
|
assert_includes response.body, 'name="connect_new_institution"'
|
|
end
|
|
|
|
test "member cannot access Sophtron account selection" do
|
|
sign_in users(:family_member)
|
|
|
|
get select_accounts_sophtron_items_url
|
|
|
|
assert_redirected_to accounts_path
|
|
end
|
|
|
|
test "cannot access another family's Sophtron item" do
|
|
other_item = families(:empty).sophtron_items.create!(
|
|
name: "Other Sophtron",
|
|
user_id: "other-developer-user",
|
|
access_key: Base64.strict_encode64("other-secret")
|
|
)
|
|
|
|
get connection_status_sophtron_item_url(other_item)
|
|
|
|
assert_response :not_found
|
|
end
|
|
|
|
test "connect_institution persists job and user institution ids" do
|
|
provider = mock
|
|
provider.expects(:create_user_institution).with(
|
|
institution_id: "inst-1",
|
|
username: "bank-user",
|
|
password: "bank-pass",
|
|
pin: ""
|
|
).returns({
|
|
JobID: "job-1",
|
|
UserInstitutionID: "ui-1"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:ensure_customer!).returns("cust-1")
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
post connect_institution_sophtron_item_url(@item), params: {
|
|
institution_id: "inst-1",
|
|
institution_name: "Example Bank",
|
|
bank_username: "bank-user",
|
|
bank_password: "bank-pass"
|
|
}
|
|
|
|
@item.reload
|
|
assert_equal "Sophtron", @item.name
|
|
assert_equal "Example Bank", @item.institution_name
|
|
assert_equal "job-1", @item.current_job_id
|
|
assert_equal "ui-1", @item.user_institution_id
|
|
assert_redirected_to connection_status_sophtron_item_path(@item)
|
|
end
|
|
|
|
test "connect_institution creates separate item for additional institution" do
|
|
@item.update!(
|
|
institution_id: "apple-inst",
|
|
institution_name: "Apple / Goldman Sachs",
|
|
user_institution_id: "ui-apple",
|
|
status: :good
|
|
)
|
|
@item.sophtron_accounts.create!(
|
|
name: "Juan",
|
|
account_id: "card-1",
|
|
currency: "USD",
|
|
balance: 1_947.18,
|
|
institution_metadata: {
|
|
name: "Apple / Goldman Sachs",
|
|
user_institution_id: "ui-apple"
|
|
}
|
|
)
|
|
|
|
provider = mock
|
|
provider.expects(:create_user_institution).with(
|
|
institution_id: "amazon-inst",
|
|
username: "bank-user",
|
|
password: "bank-pass",
|
|
pin: ""
|
|
).returns({
|
|
JobID: "job-amazon",
|
|
UserInstitutionID: "ui-amazon"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:ensure_customer!).returns("cust-1")
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
assert_difference -> { @user.family.sophtron_items.count }, 1 do
|
|
post connect_institution_sophtron_item_url(@item), params: {
|
|
institution_id: "amazon-inst",
|
|
institution_name: "Amazon",
|
|
bank_username: "bank-user",
|
|
bank_password: "bank-pass",
|
|
connect_new_institution: true
|
|
}
|
|
end
|
|
|
|
@item.reload
|
|
new_item = @user.family.sophtron_items.find_by!(user_institution_id: "ui-amazon")
|
|
|
|
assert_equal "Apple / Goldman Sachs", @item.institution_name
|
|
assert_equal "ui-apple", @item.user_institution_id
|
|
assert_equal "Amazon", new_item.institution_name
|
|
assert_equal "ui-amazon", new_item.user_institution_id
|
|
assert_equal "job-amazon", new_item.current_job_id
|
|
assert_redirected_to connection_status_sophtron_item_path(new_item, connect_new_institution: "true")
|
|
end
|
|
|
|
test "Sophtron bank credentials and mfa inputs are filtered from logs" do
|
|
parameter_filter = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
|
|
filtered_params = parameter_filter.filter(
|
|
bank_username: "bank-user",
|
|
bank_password: "bank-pass",
|
|
security_answers: [ "blue" ],
|
|
captcha_input: "captcha"
|
|
)
|
|
|
|
assert_equal "[FILTERED]", filtered_params[:bank_username]
|
|
assert_equal "[FILTERED]", filtered_params[:bank_password]
|
|
assert_equal "[FILTERED]", filtered_params[:security_answers]
|
|
assert_equal "[FILTERED]", filtered_params[:captcha_input]
|
|
end
|
|
|
|
test "create verifies credentials and persists provisioned customer id" do
|
|
stub_request(:get, "https://api.sophtron.com/api/Institution/HealthCheckAuth")
|
|
.to_return(status: 200, body: "")
|
|
stub_request(:get, "https://api.sophtron.com/api/v2/customers")
|
|
.to_return(status: 200, body: [].to_json)
|
|
stub_request(:post, "https://api.sophtron.com/api/v2/customers")
|
|
.to_return(status: 200, body: {
|
|
CustomerID: "cust-new",
|
|
CustomerName: "Sure family #{@user.family.id}"
|
|
}.to_json)
|
|
|
|
assert_difference "SophtronItem.count", 1 do
|
|
post sophtron_items_url, params: {
|
|
sophtron_item: {
|
|
name: "New Sophtron",
|
|
user_id: "developer-user",
|
|
access_key: Base64.strict_encode64("secret-key")
|
|
}
|
|
}
|
|
end
|
|
|
|
item = @user.family.sophtron_items.find_by!(name: "New Sophtron")
|
|
assert_equal "cust-new", item.customer_id
|
|
assert_redirected_to accounts_path
|
|
end
|
|
|
|
test "connection_status renders MFA challenge when Sophtron asks for security answers" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
SecurityQuestion: [ "What is your favorite color?" ].to_json,
|
|
SuccessFlag: nil,
|
|
LastStatus: "Waiting"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "What is your favorite color?"
|
|
end
|
|
|
|
test "connection_status sanitizes captcha image before rendering data uri" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
CaptchaImage: "YWJj+/=\"><svg onload=alert(1)>",
|
|
LastStatus: "Waiting"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item)
|
|
|
|
assert_response :success
|
|
captcha_src = response.body[/src="data:image\/png;base64,([^"]+)"/, 1]
|
|
assert_equal "YWJj+/=", captcha_src
|
|
assert_no_match(/svg|onload|alert|[<>"\s]/i, captcha_src)
|
|
end
|
|
|
|
test "connection_status renders token challenge before failed timeout handling" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
SuccessFlag: false,
|
|
TokenSentFlag: true,
|
|
TokenInputName: "Token",
|
|
LastStep: "TokenInput",
|
|
LastStatus: "Timeout"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Verification code"
|
|
assert_not_includes response.body, "Sophtron could not complete this connection."
|
|
end
|
|
|
|
test "connection_status times out after max UI polls" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: SophtronItemsController::CONNECTION_STATUS_MAX_POLLS)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Sophtron did not finish connecting"
|
|
assert_includes response.body, "Attempt #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS} of #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS}"
|
|
assert_equal "requires_update", @item.reload.status
|
|
assert_equal "job-1", @item.current_job_id
|
|
end
|
|
|
|
test "connection_status increments polling attempt while job is still running" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: 2)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Attempt 2 of #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS}"
|
|
assert_includes response.body, "poll_attempt=3"
|
|
assert_includes response.body, 'data-controller="polling"'
|
|
assert_includes response.body, 'data-polling-frame-id-value="modal"'
|
|
assert_includes response.body, 'data-turbo-prefetch="false"'
|
|
assert_select "a[href*='poll_attempt=3']"
|
|
end
|
|
|
|
test "connection_status keeps polling through the third initial attempt for delayed otp" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: 3)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Attempt 3 of #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS}"
|
|
assert_includes response.body, "poll_attempt=4"
|
|
assert_not_includes response.body, "Sophtron did not finish connecting"
|
|
assert_not_equal "requires_update", @item.reload.status
|
|
end
|
|
|
|
test "connection_status extends polling when Sophtron starts institution login before otp" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
LastStep: "LogInPanel",
|
|
LastStatus: "Started"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: SophtronItemsController::CONNECTION_STATUS_MAX_POLLS)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Attempt #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS} of #{SophtronItemsController::LOGIN_PROGRESS_CONNECTION_STATUS_MAX_POLLS}"
|
|
assert_includes response.body, "poll_attempt=#{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS + 1}"
|
|
assert_not_includes response.body, "Sophtron did not finish connecting"
|
|
assert_not_equal "requires_update", @item.reload.status
|
|
end
|
|
|
|
test "connection_status keeps polling after initial max when login progress was already seen" do
|
|
@item.update!(
|
|
user_institution_id: "ui-1",
|
|
current_job_id: "job-1",
|
|
raw_job_payload: {
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
LastStep: "LogInPanel",
|
|
LastStatus: "Started"
|
|
}
|
|
)
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
LastStep: "LogInPanel",
|
|
LastStatus: "Started"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: SophtronItemsController::CONNECTION_STATUS_MAX_POLLS + 1)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Attempt #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS + 1} of #{SophtronItemsController::LOGIN_PROGRESS_CONNECTION_STATUS_MAX_POLLS}"
|
|
assert_includes response.body, "poll_attempt=#{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS + 2}"
|
|
assert_not_includes response.body, "Sophtron did not finish connecting"
|
|
assert_not_equal "requires_update", @item.reload.status
|
|
end
|
|
|
|
test "connection_status uses longer polling after mfa is submitted" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
TokenInput: "123456",
|
|
LastStep: "TransactionTable",
|
|
LastStatus: "Started"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: SophtronItemsController::CONNECTION_STATUS_MAX_POLLS, post_mfa: true)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Attempt #{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS} of 15"
|
|
assert_includes response.body, "poll_attempt=#{SophtronItemsController::CONNECTION_STATUS_MAX_POLLS + 1}"
|
|
assert_not_includes response.body, "Sophtron did not finish connecting"
|
|
end
|
|
|
|
test "connection_status renders accounts when post mfa completed job has available accounts" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
TokenInput: "123456",
|
|
LastStep: "TokenInput",
|
|
LastStatus: "Completed"
|
|
})
|
|
provider.expects(:get_accounts).with("ui-1").returns({
|
|
accounts: [
|
|
{
|
|
id: "acct-1",
|
|
account_id: "acct-1",
|
|
account_name: "Sophtron Checking",
|
|
institution_name: "Example Bank",
|
|
balance: "123.45",
|
|
balance_currency: "USD",
|
|
currency: "USD",
|
|
status: "active"
|
|
}.with_indifferent_access
|
|
],
|
|
total: 1
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: 5, post_mfa: true)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Sophtron Checking"
|
|
assert_not_includes response.body, "poll_attempt=6"
|
|
|
|
@item.reload
|
|
assert_nil @item.current_job_id
|
|
assert_equal "good", @item.status
|
|
assert @item.pending_account_setup?
|
|
end
|
|
|
|
test "connection_status keeps polling when post mfa completed job has no accounts yet" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
TokenInput: "123456",
|
|
LastStep: "Reset",
|
|
LastStatus: "Completed"
|
|
})
|
|
provider.expects(:get_accounts).with("ui-1").returns({ accounts: [], total: 0 })
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: 6, post_mfa: true)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Attempt 6 of #{SophtronItemsController::POST_MFA_CONNECTION_STATUS_MAX_POLLS}"
|
|
assert_includes response.body, "poll_attempt=7"
|
|
assert_not_includes response.body, "Sophtron did not finish connecting"
|
|
assert_equal "job-1", @item.reload.current_job_id
|
|
end
|
|
|
|
test "connection_status ignores browser prefetches" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
SophtronItem.any_instance.expects(:sophtron_provider).never
|
|
|
|
get connection_status_sophtron_item_url(@item, poll_attempt: 2), headers: { "X-Sec-Purpose" => "prefetch" }
|
|
|
|
assert_response :no_content
|
|
assert_nil @item.reload.job_status
|
|
end
|
|
|
|
test "connection_status treats Sophtron timeout as failed" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:get_job_information).with("job-1").returns({
|
|
AccountID: "00000000-0000-0000-0000-000000000000",
|
|
JobType: "AddAccounts",
|
|
JobID: "job-1",
|
|
SuccessFlag: false,
|
|
LastStep: "LogInPanel",
|
|
LastStatus: "Timeout"
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
get connection_status_sophtron_item_url(@item)
|
|
|
|
assert_response :success
|
|
assert_includes response.body, "Sophtron timed out while the institution was completing login."
|
|
assert_includes response.body, "Unable to connect to the institution"
|
|
assert_includes response.body, "Bank credentials"
|
|
assert_includes response.body, "Verification code"
|
|
assert_includes response.body, "Try connecting again"
|
|
assert_not_includes response.body, "Check Provider Settings"
|
|
@item.reload
|
|
assert_equal "requires_update", @item.status
|
|
assert_nil @item.current_job_id
|
|
assert_nil @item.user_institution_id
|
|
end
|
|
|
|
test "submit_mfa sends security answer as array string" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:update_job_security_answer).with("job-1", [ "blue" ]).returns({})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
post submit_mfa_sophtron_item_url(@item), params: {
|
|
mfa_type: "security_answer",
|
|
security_answers: [ "blue" ]
|
|
}
|
|
|
|
assert_redirected_to connection_status_sophtron_item_path(@item, post_mfa: true)
|
|
end
|
|
|
|
test "submit_mfa rejects too many security answers" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:update_job_security_answer).never
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
post submit_mfa_sophtron_item_url(@item), params: {
|
|
mfa_type: "security_answer",
|
|
security_answers: Array.new(SophtronItemsController::MAX_SECURITY_ANSWERS + 1, "blue")
|
|
}
|
|
|
|
assert_redirected_to connection_status_sophtron_item_path(@item)
|
|
assert_equal "Security answers are missing or too long.", flash[:alert]
|
|
end
|
|
|
|
test "submit_mfa rejects oversized security answers" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:update_job_security_answer).never
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
post submit_mfa_sophtron_item_url(@item), params: {
|
|
mfa_type: "security_answer",
|
|
security_answers: [ "a" * (SophtronItemsController::MAX_SECURITY_ANSWER_LENGTH + 1) ]
|
|
}
|
|
|
|
assert_redirected_to connection_status_sophtron_item_path(@item)
|
|
assert_equal "Security answers are missing or too long.", flash[:alert]
|
|
end
|
|
|
|
test "submit_mfa redirects to post mfa polling window" do
|
|
@item.update!(user_institution_id: "ui-1", current_job_id: "job-1")
|
|
provider = mock
|
|
provider.expects(:update_job_token_input).with("job-1", token_input: "123456").returns({})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
|
|
post submit_mfa_sophtron_item_url(@item), params: {
|
|
mfa_type: "token_input",
|
|
token_input: "123456"
|
|
}
|
|
|
|
assert_redirected_to connection_status_sophtron_item_path(@item, post_mfa: true)
|
|
end
|
|
|
|
test "link_existing_account links manual account to sophtron account" do
|
|
@item.update!(user_institution_id: "ui-1")
|
|
account = accounts(:depository)
|
|
provider = mock
|
|
provider.expects(:get_accounts).with("ui-1").returns({
|
|
accounts: [
|
|
{
|
|
id: "acct-1",
|
|
account_id: "acct-1",
|
|
account_name: "Sophtron Checking",
|
|
balance: "123.45",
|
|
balance_currency: "USD",
|
|
currency: "USD",
|
|
account_type: "checking"
|
|
}.with_indifferent_access
|
|
],
|
|
total: 1
|
|
})
|
|
|
|
SophtronItem.any_instance.stubs(:sophtron_provider).returns(provider)
|
|
SophtronItem.any_instance.stubs(:start_initial_load_later)
|
|
|
|
assert_difference "AccountProvider.count", 1 do
|
|
post link_existing_account_sophtron_items_url, params: {
|
|
account_id: account.id,
|
|
sophtron_account_id: "acct-1"
|
|
}
|
|
end
|
|
|
|
assert account.reload.linked?
|
|
assert_equal "SophtronAccount", account.account_providers.first.provider_type
|
|
assert_redirected_to accounts_path
|
|
end
|
|
end
|