Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e067d33
Fall back to temporary `ActiveRecord::Encryption` credentials in tests
davidcornu Sep 24, 2025
af17a9f
Add a fallback value for `TEST_URL_HOST`
davidcornu Sep 24, 2025
0b39aeb
Fall back to a temporary `Lockbox` master key in tests
davidcornu Sep 24, 2025
07ea4f4
Fix `undefined local variable or method 'tagged_logger'` error
davidcornu Sep 24, 2025
9ea0cf7
Make the `AchTransfersController` spec work offline
davidcornu Sep 24, 2025
846c28c
Use cached `EuCentralBank` exchange rates for tests
davidcornu Sep 24, 2025
4e0cec6
Stub Column request in `WiresController` spec
davidcornu Sep 24, 2025
9946180
Fall back to a fake Stripe API key in tests
davidcornu Sep 24, 2025
5a5da05
Make `UsersController` spec work offline
davidcornu Sep 24, 2025
7aaf976
Make `WiresController` spec work offline
davidcornu Sep 24, 2025
6588eb5
Make `ReceiptBinMailbox` spec work offline
davidcornu Sep 24, 2025
7ac53c8
Make the `TransactionEngine::Nightly` spec work offline
davidcornu Sep 24, 2025
2f9b8e3
Make `Donation` spec work offline
davidcornu Sep 24, 2025
d6635ea
Extract `DonationSupport` and make `PayoutService::Donation::Create` …
davidcornu Sep 24, 2025
7262347
Refactor `PayoutService::Invoice::Create` so stubs work
davidcornu Sep 24, 2025
976c787
Make `Invoice` spec work offline
davidcornu Sep 24, 2025
3710f64
Expand `Sponsor` spec to cover Stripe behaviour on creation
davidcornu Sep 24, 2025
35dfc8e
Expand `StripeCardholder` spec to cover Stripe behaviour on update
davidcornu Sep 24, 2025
f6441b4
Refactor `InvoiceService::Create` spec and expand coverage
davidcornu Sep 24, 2025
e748f42
Block network calls in the test suite
davidcornu Sep 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/models/wire.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,11 @@ def usd_amount_cents
return -1 * local_hcb_code.amount_cents unless local_hcb_code.nil? || local_hcb_code.no_transactions?

eu_bank = EuCentralBank.new
eu_bank.update_rates
if Rails.env.test?
eu_bank.update_rates(Rails.root.join("spec/fixtures/files/eurofxref-daily.xml"))
else
eu_bank.update_rates
end
eu_bank.exchange(amount_cents, currency, "USD").cents
end

Expand Down
18 changes: 18 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ class Application < Rails::Application
config.active_record.encryption.deterministic_key = Credentials.fetch(:ACTIVE_RECORD, :ENCRYPTION, :DETERMINISTIC_KEY)
config.active_record.encryption.key_derivation_salt = Credentials.fetch(:ACTIVE_RECORD, :ENCRYPTION, :KEY_DERIVATION_SALT)

if Rails.env.test? && config.active_record.encryption.values_at(:primary_key, :deterministic_key, :key_derivation_salt).none?
# https://github.com/rails/rails/blob/8174a486bc3d2a720aaa4adb95028f5d03857d1e/activerecord/lib/active_record/railties/databases.rake#L527-L531
primary_key = SecureRandom.alphanumeric(32)
deterministic_key = SecureRandom.alphanumeric(32)
key_derivation_salt = SecureRandom.alphanumeric(32)

warn(<<~LOG)
⚠️ Using temporary ActiveRecord::Encryption credentials
\tprimary_key: #{primary_key.inspect}
\tdeterministic_key: #{deterministic_key.inspect}
\tkey_derivation_salt: #{key_derivation_salt.inspect}
LOG

config.active_record.encryption.primary_key = primary_key
config.active_record.encryption.deterministic_key = deterministic_key
config.active_record.encryption.key_derivation_salt = key_derivation_salt
end

# CSV previews
config.active_storage.previewers << ActiveStorage::Previewer::DocumentPreviewer

Expand Down
5 changes: 2 additions & 3 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,13 @@
config.active_support.disallowed_deprecation_warnings = []

config.action_mailer.default_url_options = {
host: Credentials.fetch(:TEST_URL_HOST)
host: Credentials.fetch(:TEST_URL_HOST, fallback: "localhost:3000"),
}
Rails.application.routes.default_url_options[:host] = Credentials.fetch(:TEST_URL_HOST)
Rails.application.routes.default_url_options[:host] = Credentials.fetch(:TEST_URL_HOST, fallback: "localhost:3000")

# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true

# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true

end
9 changes: 8 additions & 1 deletion config/initializers/lockbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,11 @@

# https://github.com/ankane/lockbox

Lockbox.master_key = Credentials.fetch(:LOCKBOX)
master_key = Credentials.fetch(:LOCKBOX)

if master_key.blank? && Rails.env.test?
master_key = Lockbox.generate_key
warn("⚠️ Using temporary Lockbox master key: #{master_key.inspect}")
end

Lockbox.master_key = master_key
10 changes: 9 additions & 1 deletion config/initializers/stripe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,12 @@

# update as needed, we specify explicitly in code to avoid inter-branch API version conflicts
Stripe.api_version = "2024-06-20"
Stripe.api_key = Credentials.fetch(:STRIPE, stripe_environment, :SECRET_KEY)

api_key = Credentials.fetch(:STRIPE, stripe_environment, :SECRET_KEY)

if api_key.blank? && Rails.env.test?
api_key = "sk_fake_#{SecureRandom.alphanumeric(32)}"
warn("⚠️ Using fake Stripe API key: #{api_key.inspect}")
end

Stripe.api_key = api_key
17 changes: 15 additions & 2 deletions spec/controllers/ach_transfers_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,28 @@
end

it "handles unknown routing numbers" do
stub_request(:get, "https://api.column.com/institutions/123456789")
.to_return(status: 400)

get :validate_routing_number, params: { value: "123456789" }

expect(response.body).to match(/"valid":false/)
expect(response.parsed_body).to eq({ "valid" => false, "hint" => "Bank not found for this routing number." })
end

it "returns bank name" do
stub_request(:get, "https://api.column.com/institutions/083000137")
.to_return_json(
status: 200,
body: {
routing_number_type: "aba",
ach_eligible: true,
full_name: "JPMORGAN CHASE BANK, NA",
}
)

get :validate_routing_number, params: { value: "083000137" }

expect(response.body).to eq({ valid: true, hint: "Jpmorgan Chase Bank, Na" }.to_json)
expect(response.parsed_body).to eq({ "valid" => true, "hint" => "Jpmorgan Chase Bank, Na" })
end
end

Expand Down
6 changes: 4 additions & 2 deletions spec/controllers/users_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

RSpec.describe UsersController do
include SessionSupport
include TwilioSupport

describe "#impersonate" do
it "allows admins to switch to an impersonated session" do
Expand Down Expand Up @@ -56,6 +57,7 @@
)
user.update!(phone_number_verified: true)
Flipper.enable(:sudo_mode_2015_07_21, user)
stub_twilio_sms_verification(phone_number: user.phone_number, code: "123456")
sign_in(user)

travel_to(3.hours.from_now)
Expand All @@ -78,8 +80,8 @@
id: user.id,
user: { use_two_factor_authentication: false },
_sudo: {
submit_method: "email",
login_code: user.login_codes.last.code,
submit_method: "sms",
login_code: "123456",
login_id: user.logins.last.hashid,
}
}
Expand Down
12 changes: 12 additions & 0 deletions spec/controllers/wires_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ def wire_params

sign_in(user)

stub_request(:get, "https://api.column.com/institutions/NOSCCATT")
.to_return_json(
status: 200,
body: { country_code: "CA" }
)

post(
:create,
params: {
Expand Down Expand Up @@ -84,6 +90,12 @@ def wire_params
expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).to include("Confirm Access")

stub_request(:get, "https://api.column.com/institutions/NOSCCATT")
.to_return_json(
status: 200,
body: { country_code: "CA" }
)

post(
:create,
params: {
Expand Down
41 changes: 41 additions & 0 deletions spec/fixtures/files/eurofxref-daily.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
<Cube time='2025-09-24'>
<Cube currency='USD' rate='1.1756'/>
<Cube currency='JPY' rate='174.51'/>
<Cube currency='BGN' rate='1.9558'/>
<Cube currency='CZK' rate='24.290'/>
<Cube currency='DKK' rate='7.4639'/>
<Cube currency='GBP' rate='0.87310'/>
<Cube currency='HUF' rate='391.08'/>
<Cube currency='PLN' rate='4.2673'/>
<Cube currency='RON' rate='5.0759'/>
<Cube currency='SEK' rate='11.0350'/>
<Cube currency='CHF' rate='0.9335'/>
<Cube currency='ISK' rate='142.40'/>
<Cube currency='NOK' rate='11.6880'/>
<Cube currency='TRY' rate='48.7289'/>
<Cube currency='AUD' rate='1.7791'/>
<Cube currency='BRL' rate='6.2240'/>
<Cube currency='CAD' rate='1.6302'/>
<Cube currency='CNY' rate='8.3786'/>
<Cube currency='HKD' rate='9.1422'/>
<Cube currency='IDR' rate='19630.64'/>
<Cube currency='ILS' rate='3.9373'/>
<Cube currency='INR' rate='104.2845'/>
<Cube currency='KRW' rate='1644.99'/>
<Cube currency='MXN' rate='21.6342'/>
<Cube currency='MYR' rate='4.9493'/>
<Cube currency='NZD' rate='2.0139'/>
<Cube currency='PHP' rate='67.568'/>
<Cube currency='SGD' rate='1.5128'/>
<Cube currency='THB' rate='37.649'/>
<Cube currency='ZAR' rate='20.3339'/>
</Cube>
</Cube>
</gesmes:Envelope>
9 changes: 9 additions & 0 deletions spec/mailboxes/receipt_bin_mailbox_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
RSpec.describe ReceiptBinMailbox do
include ActionMailbox::TestHelper
include ActionMailer::TestHelper
# The helpers above expect a tagged_logger method to be present
# See https://github.com/rspec/rspec-rails/issues/2545
attr_reader(:tagged_logger)

def receive_email(to:, from:, cc: nil)
receive_inbound_email_from_mail(
Expand All @@ -21,6 +24,12 @@ def receive_email(to:, from:, cc: nil)
)
end

before do
extract_service = instance_double(ReceiptService::Extract)
expect(extract_service).to receive(:run!).and_return(nil)
expect(ReceiptService::Extract).to receive(:new).and_return(extract_service)
end

context "with generic addresses" do
it "finds the user based on the from address" do
user = create(:user, email: "[email protected]")
Expand Down
7 changes: 6 additions & 1 deletion spec/models/donation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

require "rails_helper"

RSpec.describe Donation, type: :modal do
RSpec.describe Donation, type: :model do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

include ActiveJob::TestHelper
include DonationSupport

before do
stub_donation_payment_intent_creation
end

it "is valid" do
donation = create(:donation)
Expand Down
5 changes: 4 additions & 1 deletion spec/models/invoice_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
require "rails_helper"

RSpec.describe Invoice, type: :model do
let(:invoice) { create(:invoice) }
before do
expect_any_instance_of(Sponsor).to receive(:create_stripe_customer).and_return(true)
end

it "is valid" do
invoice = create(:invoice)
expect(invoice).to be_valid
end
end
33 changes: 31 additions & 2 deletions spec/models/sponsor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,38 @@
require "rails_helper"

RSpec.describe Sponsor, type: :model do
let(:sponsor) { create(:sponsor) }

it "is valid" do
sponsor = build(:sponsor)
expect(sponsor).to be_valid
end

it "creates a stripe customer" do
sponsor = build(:sponsor)

expect(StripeService::Customer).to(
receive(:create)
.with(
{
description: sponsor.name,
email: sponsor.contact_email,
shipping: {
name: sponsor.name,
address: {
line1: sponsor.address_line1,
line2: sponsor.address_line2,
city: sponsor.address_city,
state: sponsor.address_state,
postal_code: sponsor.address_postal_code,
country: sponsor.address_country
}
}
}
)
.and_return(Stripe::Customer.construct_from(id: "cu_1234"))
)

sponsor.save!

expect(sponsor.stripe_customer_id).to eq("cu_1234")
end
end
28 changes: 26 additions & 2 deletions spec/models/stripe_cardholder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,33 @@
require "rails_helper"

RSpec.describe StripeCardholder, type: :model do
let(:stripe_cardholder) { create(:stripe_cardholder) }

it "is valid" do
stripe_cardholder = build(:stripe_cardholder)
expect(stripe_cardholder).to be_valid
end

it "syncs Stripe on update" do
stripe_cardholder = create(:stripe_cardholder)

expect(StripeService::Issuing::Cardholder).to(
receive(:update)
.with(
stripe_cardholder.stripe_id,
{
phone_number: "+18556254225",
billing: {
address: {
line1: stripe_cardholder.address_line1,
city: stripe_cardholder.address_city,
state: stripe_cardholder.address_state,
postal_code: stripe_cardholder.address_postal_code,
country: stripe_cardholder.address_country
}
}
}
)
)

stripe_cardholder.update!(stripe_phone_number: "+18556254225")
end
end
Loading