Files
sure/test/controllers/akahu_items_controller_test.rb
T
William Wei MingandCursor d2eab1c9c7 fix(ds): resolve remaining DS Drift Patrol findings (#2157) (#2977)
* fix(ds): resolve remaining DS Drift Patrol findings from #2157

Migrate leftover hand-rolled UI and i18n defaults to DS primitives and
locale entries so missing keys raise in development again.

Closes #2157

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(a11y): name PDF import account select via aria-labelledby

Wire the existing localized heading to the select so label: false
does not leave the control unlabeled for assistive tech.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(akahu): use scoped form.select for account type fields

Replace the unusual bracketed method name on a scope-less builder with
scope: :account_types and form.select(account.id), and assert the label
for= matches the generated select id.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add German translation for shared.dot_separator

Keeps I18nTest German coverage green after merging main's completed de locale.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ds): resolve remaining DS Drift Patrol findings from #2157

Migrate leftover hand-rolled UI and i18n defaults to DS primitives and
locale entries so missing keys raise in development again.

Closes #2157

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(a11y): name PDF import account select via aria-labelledby

Wire the existing localized heading to the select so label: false
does not leave the control unlabeled for assistive tech.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(akahu): use scoped form.select for account type fields

Replace the unusual bracketed method name on a scope-less builder with
scope: :account_types and form.select(account.id), and assert the label
for= matches the generated select id.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add German translation for shared.dot_separator

Keeps I18nTest German coverage green after merging main's completed de locale.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ds): address tag select review feedback

Use the canonical focus-ring-within wrapper and preserve full width for
embedded tag search. Add the shared separator key to every shipped locale
and document the intentional unknown PDF document-type fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 07:09:01 +02:00

296 lines
9.4 KiB
Ruby

require "test_helper"
class AkahuItemsControllerTest < ActionDispatch::IntegrationTest
setup do
ensure_tailwind_build
sign_in users(:family_admin)
SyncJob.stubs(:perform_later)
@family = families(:dylan_family)
@akahu_item = AkahuItem.create!(
family: @family,
name: "Main Akahu",
app_token: "akahu-app-credential",
user_token: "akahu-user-credential"
)
@akahu_account = @akahu_item.akahu_accounts.create!(
name: "Akahu Checking",
account_id: "acc_123",
currency: "NZD"
)
@account = accounts(:depository)
end
test "setup_accounts preselects mapped account type for each account" do
AkahuItemsController.any_instance.stubs(:fetch_akahu_accounts_from_api).returns(nil)
@akahu_account.update!(account_type: "SAVINGS")
get setup_accounts_akahu_item_url(@akahu_item)
assert_response :success
select = css_select("select[name='account_types[#{@akahu_account.id}]']").first
assert select, "expected scoped account_types[#{@akahu_account.id}] select"
assert_equal 1, css_select("label[for='#{select['id']}']").size,
"label for= should match the generated select id"
selected_option = css_select("select[name='account_types[#{@akahu_account.id}]'] option[selected='selected']").first
assert_equal "Depository", selected_option["value"]
@akahu_account.update!(account_type: "FOREIGN")
get setup_accounts_akahu_item_url(@akahu_item)
assert_response :success
selected_option = css_select("select[name='account_types[#{@akahu_account.id}]'] option[selected='selected']").first
assert_equal "skip", selected_option["value"]
end
test "complete_account_setup uses Akahu account type suggestion subtype for investment accounts" do
@akahu_account.update!(account_type: "KIWISAVER")
assert_difference "Account.count", 1 do
assert_difference "AccountProvider.count", 1 do
post complete_account_setup_akahu_item_url(@akahu_item), params: {
account_types: { @akahu_account.id.to_s => "Investment" }
}
end
end
assert_redirected_to accounts_path
@akahu_account.reload
created_account = @akahu_account.current_account
assert_not_nil created_account
assert_equal "Investment", created_account.accountable_type
assert_equal "retirement", created_account.accountable.subtype
end
test "select accounts rejects unsafe return paths" do
AkahuItemsController.any_instance.stubs(:fetch_akahu_accounts_from_api).returns(nil)
unsafe_return_paths.each do |return_to|
get select_accounts_akahu_items_url, params: {
akahu_item_id: @akahu_item.id,
accountable_type: "Depository",
return_to: return_to
}
assert_response :success
assert_select %(input[name="return_to"]) do |fields|
assert fields.first["value"].blank?
end
end
end
test "select existing account rejects unsafe return paths" do
AkahuItemsController.any_instance.stubs(:fetch_akahu_accounts_from_api).returns(nil)
unsafe_return_paths.each do |return_to|
get select_existing_account_akahu_items_url, params: {
account_id: @account.id,
akahu_item_id: @akahu_item.id,
return_to: return_to
}
assert_response :success
assert_select %(input[name="return_to"]) do |fields|
assert fields.first["value"].blank?
end
end
end
test "select existing account preserves safe local return path" do
return_to = "/accounts?tab=manual"
AkahuItemsController.any_instance.stubs(:fetch_akahu_accounts_from_api).returns(nil)
get select_existing_account_akahu_items_url, params: {
account_id: @account.id,
akahu_item_id: @akahu_item.id,
return_to: return_to
}
assert_response :success
assert_select %(input[name="return_to"][value="#{return_to}"])
end
test "link accounts rejects unsafe return path on no selection redirect" do
post link_accounts_akahu_items_url, params: {
akahu_item_id: @akahu_item.id,
accountable_type: "Depository",
return_to: "https://evil.example/accounts"
}
assert_redirected_to select_accounts_akahu_items_path(
akahu_item_id: @akahu_item.id,
accountable_type: "Depository",
return_to: nil
)
end
test "link accounts rejects unsafe return path after linking" do
@akahu_account.update!(current_balance: 10)
assert_difference "AccountProvider.count", 1 do
post link_accounts_akahu_items_url, params: {
akahu_item_id: @akahu_item.id,
account_ids: [ @akahu_account.id ],
accountable_type: "Depository",
return_to: "https://evil.example/accounts"
}
end
assert_redirected_to accounts_path
end
test "link existing account rejects unsafe return paths" do
unsafe_return_paths.each_with_index do |return_to, index|
account = @family.accounts.create!(
name: "Manual Checking #{index}",
balance: 0,
currency: "NZD",
accountable: Depository.new
)
akahu_account = @akahu_item.akahu_accounts.create!(
name: "Akahu Checking #{index}",
account_id: "acc_unsafe_#{index}",
currency: "NZD"
)
assert_difference "AccountProvider.count", 1 do
post link_existing_account_akahu_items_url, params: {
account_id: account.id,
akahu_item_id: @akahu_item.id,
akahu_account_id: akahu_account.id,
return_to: return_to
}
end
assert_redirected_to accounts_path
end
end
test "preload accounts uses requested Akahu connection" do
second_item = AkahuItem.create!(
family: @family,
name: "Secondary Akahu",
app_token: "akahu-app-credential-2",
user_token: "akahu-user-credential-2"
)
AkahuItemsController.any_instance
.expects(:fetch_akahu_accounts_from_api)
.with(second_item)
.returns(nil)
get preload_accounts_akahu_items_url, params: { akahu_item_id: second_item.id }, as: :json
assert_response :success
assert_equal false, JSON.parse(response.body)["has_accounts"]
end
test "select accounts uses requested Akahu connection" do
second_item = AkahuItem.create!(
family: @family,
name: "Secondary Akahu",
app_token: "akahu-app-credential-2",
user_token: "akahu-user-credential-2"
)
second_item.akahu_accounts.create!(
name: "Secondary Checking",
account_id: "acc_secondary",
currency: "NZD"
)
AkahuItemsController.any_instance.stubs(:fetch_akahu_accounts_from_api).returns(nil)
get select_accounts_akahu_items_url, params: { akahu_item_id: second_item.id, accountable_type: "Depository" }
assert_response :success
assert_includes response.body, "Secondary Checking"
refute_includes response.body, "Akahu Checking"
end
test "link accounts uses requested Akahu connection" do
second_item = AkahuItem.create!(
family: @family,
name: "Secondary Akahu",
app_token: "akahu-app-credential-2",
user_token: "akahu-user-credential-2"
)
second_account = second_item.akahu_accounts.create!(
name: "Secondary Checking",
account_id: "acc_secondary",
currency: "NZD",
current_balance: 42
)
assert_difference "Account.count", 1 do
assert_difference "AccountProvider.count", 1 do
post link_accounts_akahu_items_url, params: {
akahu_item_id: second_item.id,
account_ids: [ second_account.id ],
accountable_type: "Depository"
}
end
end
assert_redirected_to accounts_path
assert_equal second_account.id, AccountProvider.order(:created_at).last.provider_id
assert_nil @akahu_account.reload.current_account
end
test "select existing account uses requested Akahu connection" do
second_item = AkahuItem.create!(
family: @family,
name: "Secondary Akahu",
app_token: "akahu-app-credential-2",
user_token: "akahu-user-credential-2"
)
second_item.akahu_accounts.create!(
name: "Secondary Checking",
account_id: "acc_secondary",
currency: "NZD"
)
AkahuItemsController.any_instance.stubs(:fetch_akahu_accounts_from_api).returns(nil)
get select_existing_account_akahu_items_url, params: {
account_id: @account.id,
akahu_item_id: second_item.id
}
assert_response :success
assert_includes response.body, "Secondary Checking"
refute_includes response.body, "Akahu Checking"
end
test "complete account setup hides raw creation errors from users" do
raw_message = "raw provider failure with akahu-user-credential"
AkahuItemsController.any_instance
.stubs(:create_account_from_akahu)
.raises(ActiveRecord::RecordNotSaved.new(raw_message))
post complete_account_setup_akahu_item_url(@akahu_item), params: {
account_types: { @akahu_account.id.to_s => "Depository" }
}
assert_redirected_to accounts_path
assert_equal I18n.t("akahu_items.complete_account_setup.creation_failed"), flash[:alert]
refute_includes flash[:alert], raw_message
end
private
def unsafe_return_paths
[
"https://evil.example/accounts",
"http://evil.example/accounts",
"//evil.example/accounts",
"\\evil.example/accounts",
"/\\evil.example/accounts",
"/%2fevil.example/accounts",
"/%2Fevil.example/accounts",
"/%5cevil.example/accounts",
"/%5Cevil.example/accounts",
"/\naccounts",
"/ accounts",
" "
]
end
end