rsepc example for testing file manipulation

35 views
Skip to first unread message

Johan De Wit

unread,
Jul 25, 2014, 8:08:59 AM7/25/14
to puppe...@googlegroups.com
Hi,

Does someone knows more rspec code examples for working with files ?

I stumbled across
https://github.com/puppetlabs/puppetlabs-inifile/blob/master/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
,

but cannot find more examples, and struggling a bit in writing good
rspec tests for the following code snippet :

(intention is to read the uid from a yaml file for users)

require 'yaml'
module Puppet::Parser::Functions
newfunction(:lookup_uid, :type => :rvalue ) do |args|

# read the configuration file from puppets configuration dir
configfile = File.join([File.dirname(Puppet.settings[:config]),
'lookup_uid.yaml'])
raise(Puppet::ParseError, "lookup_uid configfile not readable")
unless File.exist?(configfile)
config = YAML.load_file(configfile)

# read the user yaml file
raise(Puppet::ParseError, "Could not load user mapping file
(#{config['user_file']}") unless File.exists?(config['user_file'])

# Lookup the uid, and convert it to an integer
users = YAML.load_file(config['user_file'])
uid = users.fetch(args[0], ifnone = nil)
raise(Puppet::ParseError, "#{args[0]} user not found in
#{config['user_file']}") unless uid
uid.to_int
end
end

Thxs

johan

--
Johan De Wit

Open Source Consultant

Red Hat Certified Engineer (805008667232363)
Puppet Certified Professional 2013/2014 (PCP0000006)
_________________________________________________________

Open-Future Phone +32 (0)2/255 70 70
Zavelstraat 72 Fax +32 (0)2/255 70 71
3071 KORTENBERG Mobile +32 (0)474/42 40 73
BELGIUM http://www.open-future.be
_________________________________________________________


Next Events:
Linux Training | https://www.open-future.be/linux-training-8-till-12th-september
Puppet Introduction Course | https://www.open-future.be/puppet-introduction-course-15th-september
Puppet Fundamentals Training | https://www.open-future.be/puppet-fundamentals-training-16-till-18th-september
Zabbix Certified Specialist | https://www.open-future.be/zabbix-certified-specialisttraining-22-till-24th-september
Zabbix Certified Professional | https://www.open-future.be/zabbix-certified-professional-training-25-till-26th-september
Subscribe to our newsletter | http://eepurl.com/BUG8H

Andy Parker

unread,
Jul 25, 2014, 1:47:50 PM7/25/14
to puppe...@googlegroups.com
Hi Johan,

On Fri, Jul 25, 2014 at 5:08 AM, Johan De Wit <Jo...@open-future.be> wrote:
Hi,

Does someone knows more rspec code examples for working with files ?

I stumbled across https://github.com/puppetlabs/puppetlabs-inifile/blob/master/spec/unit/puppet/provider/ini_setting/ruby_spec.rb ,

but cannot find more examples, and struggling a bit in writing good rspec tests for the following code snippet :


You essentially have two choices when doing this kind of thing: use a real filesystem or stub it out in some way. The code that you pointed to looks to be using a real filesystem. In that case what is often a good practice is to create a temp directory in a setup step (before :each) and delete it in a teardown (after :each). Then you do whatever is needed to have your code read from that directory. In your case it looks like you should also set Puppet[:config] to be some fake file in that directory. Then write out the files that you need for your check (or don't write out the file if you want to test the File.exist? == false paths).

If you are going to go down the stubbing route you'll have a much harder time. You can use something like fakefs (a ruby gem for testing against filesystems), but since you are pulling in all of puppet for this you might encounter a lot of problems as it tries to read things that you don't care about at all. If you are going to head down that path, then I suggest extracting your functionality out of the puppet function, test it in isolation, and then make the puppet function a very small adapter to the other code (essentially just a single call). Then you can test your logic without any ties to puppet.
 
(intention is to read the uid from a yaml file for users)

require 'yaml'
module Puppet::Parser::Functions
  newfunction(:lookup_uid, :type => :rvalue ) do |args|

    # read the configuration file from puppets configuration dir
    configfile = File.join([File.dirname(Puppet.settings[:config]), 'lookup_uid.yaml'])
    raise(Puppet::ParseError, "lookup_uid configfile not readable") unless File.exist?(configfile)
    config = YAML.load_file(configfile)

    # read the user yaml file
    raise(Puppet::ParseError, "Could not load user mapping file (#{config['user_file']}") unless File.exists?(config['user_file'])

    # Lookup the uid, and convert it to an integer
    users = YAML.load_file(config['user_file'])
    uid = users.fetch(args[0], ifnone = nil)
    raise(Puppet::ParseError, "#{args[0]} user not found in #{config['user_file']}") unless uid
    uid.to_int
  end
end


In the code itself there are a few things that caught my eye. Your tests might have uncovered some of these.

require 'yaml'
# no need to open the module, just call the function
# you can use arity to have it do some parameter checks for you (assuming it'll be used on a new enough puppet)
Puppet::Parser::Functions.newfunction(:lookup_uid, :type => :rvalue, :arity => 1) do |args|
  # give the parameter a meaningful name as soon as possible
  username = args[0]

  # don't need to construct an array in the join() call
  # also, why :config and not :confdir?
  # Settings can be accessed by just using Puppet[:settingname] instead of Puppet.settings[:settingname]
  configfile = File.join(File.dirname(Puppet[:config]), 'lookup_uid.yaml')

  # I always prefer stating things in the positive and using a full if ... else ... end. I find that it makes the various code paths and error cases much more visible.
  if File.exist?(configfile)
    # this can raise errors, you might consider dealing with them here and providing some context to the user in the error message, just don't lose the original error!
    config = YAML.load_file(configfile)

    # once again, prefer positive statements
    # config could be nil, and 'user_file' could be nil (or not a string)
    # there are actually more ways that this could be validated...isn't input validation fun?
    if config.is_a?(Hash) && config['user_file'].is_a?(String) && File.exists?(config['user_file'])
      users = YAML.load_file(config['user_file'])
      # passing nil in the second position to fetch() is the same as just fetching and getting nothing (since it is the default)
      uid = users[username]
      if uid
        # adding some error checking around this might be good. It is possible that it can't be converted to an int
        uid.to_int
      else
        raise(Puppet::ParseError, "#{username} user not found in #{config['user_file']}")
      end
    else
      # with all of the extra validation above, it is clear that this isn't enough of an error message, in fact the validation might be something to do as a separate step and be able to signal each possible error individually
      raise(Puppet::ParseError, "Could not load user mapping file (#{config['user_file']}")
    end
  else
    # In errors be as precise and convey as much information as possible. In this case, what file did it look for. Also, the check was for existence, so you can only inform the user about that state of things.
    raise(Puppet::ParseError, "lookup_uid configfile #{configfile} does not exist")
  end
end
 
Thxs

johan

--
Johan De Wit

Open Source Consultant

Red Hat Certified Engineer              (805008667232363)
Puppet Certified Professional 2013/2014 (PCP0000006)
_________________________________________________________
 Open-Future                 Phone     +32 (0)2/255 70 70
Zavelstraat 72              Fax       +32 (0)2/255 70 71
3071 KORTENBERG             Mobile    +32 (0)474/42 40 73
BELGIUM                     http://www.open-future.be
_________________________________________________________
 
Next Events:
Linux Training | https://www.open-future.be/linux-training-8-till-12th-september
Puppet Introduction Course | https://www.open-future.be/puppet-introduction-course-15th-september
Puppet Fundamentals Training | https://www.open-future.be/puppet-fundamentals-training-16-till-18th-september Zabbix Certified Specialist | https://www.open-future.be/zabbix-certified-specialisttraining-22-till-24th-september
Zabbix Certified Professional | https://www.open-future.be/zabbix-certified-professional-training-25-till-26th-september
Subscribe to our newsletter | http://eepurl.com/BUG8H


--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-dev/53D248D8.9020304%40open-future.be.
For more options, visit https://groups.google.com/d/optout.



--
Andrew Parker
Freenode: zaphod42
Twitter: @aparker42
Software Developer

Join us at PuppetConf 2014September 22-24 in San Francisco
Register by May 30th to take advantage of the Early Adopter discount save $349!

Johan De Wit

unread,
Jul 26, 2014, 9:47:49 AM7/26/14
to puppe...@googlegroups.com, andy@puppetlabs.com >> Andy Parker
Hi Andy,

thanks for the great explanation.

I really need to get my Dev skills above zero :) 

I will post a link to this function and write some step by step little guide ....


grts

johan
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+...@googlegroups.com.



--
Andrew Parker
Freenode: zaphod42
Twitter: @aparker42
Software Developer

Join us at PuppetConf 2014September 22-24 in San Francisco
Register by May 30th to take advantage of the Early Adopter discount save $349!
--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-dev/CANhgQXtapXGjYun92fLqkMZm%2B2OOVqdKpDDUpR5NQqj%2BroYRRA%40mail.gmail.com.

For more options, visit https://groups.google.com/d/optout.


-- 
Johan De Wit

Open Source Consultant

Red Hat Certified Engineer              (805008667232363)
Puppet Certified Professional 2013/2014 (PCP0000006)
_________________________________________________________
 
Open-Future                 Phone     +32 (0)2/255 70 70
Zavelstraat 72              Fax       +32 (0)2/255 70 71
3071 KORTENBERG             Mobile    +32 (0)474/42 40 73
BELGIUM                     http://www.open-future.be
_________________________________________________________
 

  

Upcoming Events:

Reply all
Reply to author
Forward
0 new messages