RailsSettings vs parallel_tests

RailsSetting stubbing problem

I have an area of functionality that is controlled by a Settings class that is a subclass of RailsSettings::CachedSettings (rails-settings-cached gem) and changing that setting for tests was interfering with the parallel_tests gem when I ran my rspec tests.

I tried namespacing the cache, making TTL 0, invalidating the cache, monkeypatching the lookup... Every convoluted solution possible... to try and get tests to play nicely together.

The humbling thing about programming is how simple actual solutions are compared the things the brain comes up with.

The solution? Wrap the access to the Setting in a layer of abstraction (which I had partially done already.)

class ThatFunctionality
  def self.enabled?
    Setting['that_functionality.enabled']
  end
  def self.enabled=(value)
    Setting['that_functionality.enabled']=value
  end

And then in tests:

before do
  allow(ThatFunctionality).to receive(:enabled?).and_return(true) # or false
end

Trying again to stub RailsSettings

The class method approach is explicit, but once a large set of settings is needed, it can get unwieldy. If you haven't found a better strategy for wrapping settings values, you can go back to stubbing the :[] method. The caveat is that you need to save the original Settings behavior for any settings that you want to leave unchanged. By stubbing the :[] first to passthrough to .and_call_original, you can add additional stubs on top.

Hooking in an LDAP Test Server to Cucumber Tests

I managed to get a custom Devise strategy with LDAP working, but had no clear way of automating tests. I wanted to validate if I still had to keep the password fresh in the database, and needed to be able to write scenarios around that in case someone attempted to refactor out the code.

After trying to incorporate the spec strategy used in the development devise_ldap_authenticatable and failing, I found a ruby wrapper of ApacheDS called ladle that looked like it would serve my purposes.

I included in gem in my test group in my Gemfile:

  gem 'ladle'

At the top of my features/env.rb file for configuring cucumber, I turned off admin binding (wanted the connection as simple as possible):

::Devise.ldap_use_admin_to_bind = false

I then created an @ldap tag for my LDAP-dependent features than would start and stop the LDAP server in those instances. (Again, in my features/env.rb... probably need to clean that up.)

Around('@ldap') do |scenario, block|
  $ladle ||= Ladle::Server.new(
    :ldif => "spec/ldap/test_users.ldif",
    :domain => "dc=example,dc=org",
    :quiet => true
  )
  $ladle.start
  block.call
  $ladle.stop
end

I then created an the spec/ldap/test_users.ldif (from following the example in the ladle project).

version: 1
 
dn: ou=people,dc=example,dc=org
objectClass: top
objectClass: organizationalUnit
ou: people
 
dn: uid=eadmin,ou=people,dc=example,dc=org
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Example Admin
sn: Admin
givenName: Example
mail: eadmin@example.com
uid: eadmin
# Password is "b44b44bl@cksh33p!"
userpassword: {SHA}Aedq5WHQSxglvJSfpX0kgdGRdHk=

I generated the password with:

  slappasswd -h {SHA} -s b44b44bl@cksh33p!

One stupid mistake that I did in the process was kicking off two Ladle servers (with slightly different parameters). In one iteration, I couldn't bind to the user. Another, the server using the test file failed to start. Be aware that Ladle will run happily with default parameters, but that they won't be much use to you.

If you want to test your configuration file:

require 'net/ldap'
require 'ladle'
 
$ladle ||= Ladle::Server.new(
  :ldif => "spec/ldap/test_users.ldif",
  :domain => "dc=example,dc=org",
  :quiet => true
)
$ladle.start
 
ldap = Net::LDAP.new(host: 'localhost',
    :port => 3897,
)
filter = Net::LDAP::Filter.eq('mail', 'eadmin@example.com') # or ('uid', 'eadmin') 
 
ldap.search(:base => 'ou=people,dc=example,dc=org', :filter => filter) do |entry|
  ldap.auth(entry.dn, 'b44b44bl@cksh33p!') # or whatever your password is
 
  entry.each do |attribute, values|
    puts "   #{attribute}:"
    values.each do |value|
      puts "      --->#{value}"
    end
  end
end