Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to stub Rails credential key? #2099

Closed
olimart opened this issue Mar 14, 2019 · 17 comments
Closed

Is there a way to stub Rails credential key? #2099

olimart opened this issue Mar 14, 2019 · 17 comments
Assignees

Comments

@olimart
Copy link

olimart commented Mar 14, 2019

Similar to stubbing environment variable, can we stub Rails credential key?

What Ruby, Rails and RSpec versions are you using?

Ruby version: 2.6.1
Rails version: 5.2.2
Rspec version: 3.8

Observed behaviour

allow(Rails.application.credentials).to receive(:my_token).and_return('')

Expected behaviour

allow(Rails.application.credentials).to receive(:my_token).and_return('123')
Rails.application.credentials.my_token
=> '123'
@JonRowe JonRowe closed this as completed Mar 14, 2019
@JonRowe
Copy link
Member

JonRowe commented Mar 14, 2019

As credentials is an object you have to stub what you wish to retrieve. So:

allow(Rails.application.credentials).to receive(:my_token).and_return('123')
Rails.application.credentials.my_token
# => 123

allow(Rails.application.credentials).to receive(:[]).with(:my_token).and_return('345')
Rails.application.credentials[:my_token]
# => 345

It'd be better to use it as designed and setup a test environment set of credentials.

@olimart
Copy link
Author

olimart commented Mar 14, 2019

Thanks @JonRowe I edited the post to actually focus on the issue (described in your first example).

my_token is already defined in credentials.yml.enc but I need to override the value in my specs.

I tried indeed the following but it keeps the value defined in credentials.yml.enc.

allow(Rails.application.credentials).to receive(:my_token).and_return('123')
Rails.application.credentials.my_token
# => value_from_credentials_file

@benoittgt
Copy link
Member

benoittgt commented Mar 14, 2019

I just did a test on a fresh_app

And I had no issue.

My credentials file looks like this:

 aws:
  access_key_id: 123
  secret_access_key: 345
olimart: 'from_config'

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: xxxx
require 'rails_helper'

describe 'Credentials' do
  it 'stubs credentials' do
    allow(Rails.application.credentials).to receive(:olimart).and_return('123')
    expect(Rails.application.credentials.olimart).to eq('123')
  end
end

no issues.

@olimart
Copy link
Author

olimart commented Mar 14, 2019

@benoittgt What's your master key?

Amazing. The exact same code fails on my end. I appreciate your help.

@JonRowe
Copy link
Member

JonRowe commented Mar 14, 2019

@benoittgt I'll leave it up to you to decide if this should be reopened :)

@olimart
Copy link
Author

olimart commented Mar 15, 2019

I'll leave it up to you to decide if this should be reopened :)

don't think so. Just need to find out why but in the meantime we came up with a workaround.

@Uelb
Copy link

Uelb commented Apr 4, 2019

Actually, I have the exact same issue, and I cannot figure out why, @olimart, what is the workaround you implemented ?

@olimart
Copy link
Author

olimart commented Apr 5, 2019

@Uelb

shared_context 'encrypted configuration' do
  def stub_credential(key, value)
    allow(Rails.application).to receive(:credentials).and_return(OpenStruct.new(key.to_sym => value))
  end
end

# usage
stub_credential(:my_credential, 123)

@jcquarto
Copy link

jcquarto commented Aug 1, 2020

what is the usage for a deeper structure, for example if the credentials.yml file has

test:
level:
MY_DEEP_ENV_VAR: 12345

(sorry, the above should be indented as per a .yml file

would you suggest:
stub_credential([:test][:level][:MY_DEEP_ENV_VAR], 67890) ??

@pirj
Copy link
Member

pirj commented Aug 1, 2020

  allow(Rails.application).to receive_message_chain(:test, :level, :MY_DEEP_ENV_VAR => 67890)

@Petercopter
Copy link

Rubocop was complaining about receive_message_chain, so I think I'm going to go with

allow(Rails.application).to receive(:credentials).and_return(JSON.parse({ checkr: { secret: 'super duper secret' } }.to_json, object_class: OpenStruct))

@jairovm
Copy link

jairovm commented Aug 27, 2021

My 2 cents

  def stub_credential(**credentials)
    allow(Rails.application).to receive(:credentials).and_return(
      OpenStruct.new(**credentials)
    )
  end
  
  stub_credential(credential_one: 1, credential_two: 2)

@paulodeon
Copy link

This worked for me, note the use of deep_merge:

allow(Rails.application).to receive(:credentials).and_return(Rails.application.credentials.deep_merge(test: { stripe: { trial_periods: { standard_monthly_uk: 30 } } }))

@pirj
Copy link
Member

pirj commented Sep 8, 2023

We reserve our issue tracker for features and bugs.
I suggest seeking help using resources outlines in https://rspec.info/help
Thanks for understanding.

@JonRowe
Copy link
Member

JonRowe commented Sep 8, 2023

You will find it hard to mock out code run in class definitions, RSpec doesn't control the loading of classes (this is typically something Rails does for you) so by the time the class is loaded the constant is already set, you can use the stub_const approach but the error is because in your test environment you don't have a vendor_api set, you need a fake credential set for the class to load, then you can stub the constant.

@prpetten
Copy link

prpetten commented Sep 8, 2023

@JonRowe Thanks

@rohitjoshixyz
Copy link

rohitjoshixyz commented Apr 11, 2024

I found an elegant way to stub a deeply nested rails credential in Rspec, commenting here so that someone else can benefit from it.

Suppose you have the following in your rails credentials.yml file

cloudflare:
  my_team:
    my_api_token: 1234
    my_zone_id: zone-id

You can mock it so as

allow(Rails.application.credentials.cloudflare).to receive(:dig).and_call_original
allow(Rails.application.credentials.cloudflare).to receive(:dig).with(:my_team, :my_zone_id).and_return("zone-id")

The first line will ensure that other nested keys are not mocked and will be called as originally expected.
Second line will ensure that in your spec the mocked value zone_id is used

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests