How can I write spec tests for puppet facts? - ruby

I have the following rb script which generate puppet facts according to the packages installed and which seems to be working ok in my puppet environment:
begin
pack = Facter::Core::Execution.execute('rpm -qa | grep ^ts')
packages = pack.split("\n")
packagehash = Hash.new
packages.each do |f|
packagehash[f.split("-")[0]] = f.split("-")[1] + ("-") + f.split("-")[2].split(".")[0]
end
rescue
end
begin
unless packagehash.empty?
packagehash.each_pair do |k,v|
Facter.add("bs_rpm_#{k}") {
setcode { "#{v}" }
}
end
end
rescue
end
I wrote the following spec which runs a small dummy test to see if my rspec env in general is ok:
require 'spec_helper'
describe 'bs package spec' do
before do
Facter.fact(:kernel).stubs(:value).returns("windows")
end
it "should run windows" do
Facter.fact(:kernel).value.should == "windows"
end
it "should create new facts" do
Facter::Core::Execution.stubs(:execute).with('rpm -qa | grep ^ts').returns('ts3_hostt01-1.0.0-34.x86_64\n')
Facter.fact(:bs_rpm_ts3_hostt01).value.should == "1.0.0-34"
end
end
But then when running rake spec I get the following error:
[dan#kyvltvm00022 bs_master]$ rake spec
/home/dan/.rvm/rubies/ruby-2.1.0/bin/ruby -S rspec spec/unit/facter/bs_package_spec.rb --color
.F
Failures:
1) bs package spec should create new facts
Failure/Error: Facter::Core::Execution.stubs(:execute).with('rpm -qa | grep ^ts').returns('ts3_hostt01-1.0.0-34.x86_64\n')
NameError:
uninitialized constant Facter::Core
# ./spec/unit/facter/bs_package_spec.rb:13:in `block (2 levels) in <top (required)>'
Finished in 0.00692 seconds
2 examples, 1 failure
Failed examples:
rspec ./spec/unit/facter/bs_package_spec.rb:12 # bs package spec should create new facts
/home/dan/.rvm/rubies/ruby-2.1.0/bin/ruby -S rspec spec/unit/facter/bs_package_spec.rb --color failed
[dan#kyvltvm00022 bs_master]$ exit
shell returned 1
[dan#kyvltvm00022 bs_master]$
What am I doing wrong or might be missing that is not loading Facter::Core ?? My spec_helper looks like this:
[dan#kyvltvm00022 bs_master]$ cat spec/spec_helper.rb
dir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift File.join(dir, 'lib')
# Don't want puppet getting the command line arguments for rake or autotest
ARGV.clear
require 'puppet'
require 'facter'
require 'mocha'
gem 'rspec', '>=2.0.0'
require 'rspec/expectations'
require 'puppetlabs_spec_helper/puppet_spec_helper'
RSpec.configure do |config|
# FIXME REVISIT - We may want to delegate to Facter like we do in
# Puppet::PuppetSpecInitializer.initialize_via_testhelper(config) because
# this behavior is a duplication of the spec_helper in Facter.
config.before :each do
# Ensure that we don't accidentally cache facts and environment between
# test cases. This requires each example group to explicitly load the
# facts being exercised with something like
# Facter.collection.loader.load(:ipaddress)
Facter::Util::Loader.any_instance.stubs(:load_all)
Facter.clear
Facter.clear_messages
end
end
[dan#kyvltvm00022 bs_master]$
[UPDATE]
After checking my test system I noticed the facter gem was missing core so I updated the code and test as follows:
pack is now:
pack = Facter::Util::Resolution.exec('rpm -qa | grep ^ts')
and the stub in my test now is:
Facter::Util::Resolution.stubs(:exec).with('rpm -qa | grep ^ts').returns('ts3_hostt01-1.0.0-34.x86_64\n')
And the result now is this:
[dan#kyvltvm00022 bs_master]$ rake spec
/home/dan/.rvm/rubies/ruby-2.1.0/bin/ruby -S rspec spec/unit/facter/bs_package_spec.rb --color
.F
Failures:
1) bs package spec should create new facts
Failure/Error: Facter.fact(:bs_rpm_ts3_hostt01).value.should == "1.0.0-34"
NoMethodError:
undefined method `value' for nil:NilClass
# ./spec/unit/facter/bs_package_spec.rb:14:in `block (2 levels) in <top (required)>'
Finished in 0.00747 seconds
2 examples, 1 failure
Failed examples:
rspec ./spec/unit/facter/bs_package_spec.rb:12 # bs package spec should create new facts
/home/dan/.rvm/rubies/ruby-2.1.0/bin/ruby -S rspec spec/unit/facter/bs_package_spec.rb --color failed
[dan#kyvltvm00022 bs_master]$
What am i doing wrong in my test?

Looking through the facter code, this is the file you're requiring:
https://github.com/puppetlabs/facter/blob/master/lib/facter.rb
It doesn't require core/execution itself... without digging too much further in could you just try this in your spec_helper:
require 'facter/core/execution'

Related

Ruby LoadError with gmp

$ gem install gmp
Building native extensions. This could take a while...
Successfully installed gmp-0.7.43
Parsing documentation for gmp-0.7.43
Done installing documentation for gmp after 0 seconds
1 gem installed
$ cat gmp-test.rb
require 'gmp'
$ /opt/src/ruby-3.2.0/bin/ruby gmp-test.rb
<internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- /home/dunham/.local/share/gem/ruby/3.2.0/gems/gmp-0.7.43/lib/../ext/gmp (LoadError)
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
from /home/dunham/.local/share/gem/ruby/3.2.0/gems/gmp-0.7.43/lib/gmp.rb:9:in `<top (required)>'
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `require'
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `rescue in require'
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:149:in `require'
from gmp-test.rb:1:in `<main>'
<internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- gmp (LoadError)
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
from gmp-test.rb:1:in `<main>'
Library seems to be dead. It's expecting gmp.so to be in ext directory but it ends up in lib directory, it's probably new rubygems doing things differently.
>> require "gmp"
<internal:/home/alex/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in 'require':
cannot load such file -- /home/alex/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/gmp-0.7.43/lib/../ext/gmp (LoadError)
$ ls $(dirname $(gem which gmp))
gmp.rb gmp.so
# move ^ that to ../ext/
$ mv $(dirname $(gem which gmp))/gmp.so $(dirname $(gem which gmp))/../ext/
>> require "gmp"
=> true
>> GMP::Z(0)
=> 0
Update
From the linked logs looks like you're still not loading gmp.so which is where all the classes are defined. You can copy gmp.so into your app:
# copy `gmp.rb`
$ cp $(gem which gmp) .
# copy `gmp.so`
$ cp $(dirname $(gem which gmp))/gmp.so .
# or if you moved it to ext
$ cp $(dirname $(gem which gmp))/../ext/gmp.so .
$ touch app.rb
$ ls
app.rb gmp.rb gmp.so
gmp.rb defines GMP.sprintf method, if this is not a required method and you don't use it, you can remove this file.
# gmp.rb
# require 'rbconfig'
#
# ENV['PATH'] = [File.expand_path(
# File.join(File.dirname(__FILE__), "..", "ext")
# ), ENV['PATH']].compact.join(';') if RbConfig::CONFIG['host_os'] =~ /(mswin|mingw|mingw32)/i
#
# require File.dirname(__FILE__) + '/../ext/gmp'
# unless RUBY_VERSION =~ /^1.8/
module GMP
def self.sprintf(format, *args)
first_pct = format.index '%'
result = format[0...first_pct]
#format.gsub(/(?<!%)%[0#+ ']*[0-9]*.?[0-9]*[a-zA-Z][^%]*/) do |fragment|
format.gsub(Regexp.new('(?<!%)%[0#+ \']*[0-9]*.?[0-9]*[a-zA-Z][^%]*')) do |fragment|
arg = args.shift
if fragment =~ /%[0#+ ']*[0-9]*.?[0-9]*[ZQF]/
result << sprintf2(fragment, arg)
elsif fragment =~ /%[0#+ ']*[0-9]*.?[0-9]*[PR]/ && GMP.const_defined?(:MPFR_VERSION)
result << GMP::F.sprintf2(fragment, arg)
else
result << (fragment % arg)
end
end
result
end
end
# end
# app.rb
require_relative "gmp.so"
p GMP::Z
p GMP::Z(0)
p GMP::Q
p GMP::F
require_relative "gmp.rb"
p GMP.sprintf "%Zd", GMP.Z(0)
$ ruby --yjit -v
ruby 3.2.0 (2022-12-25 revision a528908271) +YJIT [x86_64-linux]
$ ruby --yjit app.rb
GMP::Z
0
GMP::Q
GMP::F
"0"

RSpec - Rerun failed tests that are also tagged?

We have written a large number of selenium UI tests that very occasionally fail due to dumb reasons like a web server being too slow. Additionally, we make heavy use of rspec tags to only run the tests that we want to run. We want to retry ONLY the tests that are tagged with something AND that have previously failed. In the following example, running rspec --tag all --only_failures runs the last two tests, when I only want it to run the 2nd test.
spec_helper.rb
RSpec.configure do |config|
config.example_status_persistence_file_path = "RunResults.txt"
end
test_spec.rb
require 'rspec'
require 'spec_helper'
require './test'
RSpec.describe Test do
it "should pass", :all, :one do
expect(1).to eql(1)
end
it "should fail", :all, :two do
expect(1).to eql(2)
end
it "should fail", :three do
expect(1).to eql(2)
end
end
RunResults.txt
example_id | status | run_time |
------------------------ | ------ | --------------- |
./spec/test_spec.rb[1:1] | passed | 0.00055 seconds |
./spec/test_spec.rb[1:2] | failed | 0.01796 seconds |
./spec/test_spec.rb[1:3] | failed | 0.00017 seconds |
RSpec
$ rspec --tag all --only_failures
Run options: include {:all=>true, :last_run_status=>"failed"}
.FF
Failures:
1) Test should fail
Failure/Error: expect(1).to eql(2)
expected: 2
got: 1
(compared using eql?)
# ./spec/test_spec.rb:11:in `block (2 levels) in <top (required)>'
2) Test should fail
Failure/Error: expect(1).to eql(2)
expected: 2
got: 1
(compared using eql?)
# ./spec/test_spec.rb:15:in `block (2 levels) in <top (required)>'
Finished in 0.01949 seconds (files took 0.24733 seconds to load)
3 examples, 2 failures
Failed examples:
rspec ./spec/test_spec.rb:10 # Test should fail
rspec ./spec/test_spec.rb:14 # Test should fail

Chef::DataBag and RSpec-mocks -- undefined method `recieve'

i am trying to create a list method stub for Chef::DataBag class as follows:
$ cat spec/spec_helper.rb
require "rspec"
RSpec.configure { |config|
config.mock_framework = :rspec
config.mock_with :rspec
}
$ cat spec/test_spec.rb
require "spec_helper"
require "chef"
RSpec.describe "test" do
describe "simple test" do
it "mocks chef databag" do
allow(Chef::DataBag).to recieve(:list).and_return {}
end
end
end
$ grep "rspec \|chef " Gemfile.lock | head -n 2
gem "chef", "11.16.4"
gem "rspec", "3.1.0"
when executing rspec i get the following error.
$ bundle exec rspec spec/test_spec.rb
F
Failures:
1) test simple test mocks chef databag
Failure/Error: allow(Chef::DataBag).to recieve(:list).and_return {}
NoMethodError:
undefined method `recieve' for #<RSpec::ExampleGroups::Test::SimpleTest:0x000000049964f8>
# ./spec/test_spec.rb:7:in `block (3 levels) in <top (required)>'
Finished in 0.00048 seconds (files took 0.53998 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/test_spec.rb:6 # test simple test mocks chef databag
that is how i verified that Chef::DataBag has the list method:
$ bundle exec ruby -e "require 'chef'; puts Chef::DataBag.methods" | grep list
list
can you please point out what is wrong?
You misspelled "receive." Try this:
allow(Chef::DataBag).to receive(:list).and_return {}

Run def in ruby program

I'm a beginner in programming and wrote this little program:
Test.rb:
# encoding: utf-8
require 'open-uri'
require 'nokogiri'
def parse_file
doc = Nokogiri::XML(File.open("test.xml"))
parse_xml(doc)
end
def parse_xml(doc)
doc.root.elements.each do |node|
parse_tracks(node)
end
end
def parse_tracks(node)
if node.node_name.eql? 'kapitel'
puts 'New Kapitel'
end
end
I know how to execute this code:
ruby test.rb
But how can I call the def parse_file?
Simply add whatever you want to the end of your file. Ruby scripts are simply scripts, they are being interpreted:
…
end
parse_file # ⇐ HERE YOU GO
You can either call the method at the end of your test.rb file:
def parse_file
# ...
end
parse_file
And run it with
$ ruby test.rb
Or leave the file as it is, require it as a library and call the method manually:
$ ruby -r test.rb -e "parse_file"
Rather than hard-coding your file path, you can pass it as an argument when calling your script. Arguments can be accessed via the ARGV array:
def parse_file(file)
doc = Nokogiri::XML(File.open(file))
parse_xml(doc)
end
parse_file(ARGV.first)
Now you can run it with:
$ ruby test.rb test.xml
Another option is to make the script executable. Add a shebang as the first line of you file:
#!/usr/bin/env ruby
And set the execute flag:
$ chmod +x test.rb
Now you can run it with:
$ ./test.rb test.xml
just add
parse_file
in the end of your ruby file

Running command line commands from Thor executable

In my executable Ruby file I have the following:
#!/usr/bin/env ruby
require 'thor'
include Thor::Actions
class UI < Thor
# def self.source_root
# File.dirname(__FILE__)
# end
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcalmakecal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
In the terminal when I run the file as is I get an empty line as Thor's run command is returning a NilClass.
However, when I un-comment the puts `ls ~` and comment out Thor's run method I get an output of my home directory as expected.
I'm having trouble figuring out why I can't get Thor's run method to work like Ruby's ticks.
Any ideas where I may have went wrong?
Thanks for looking
I didn't put the include statement inside my class and that messed things up. The code should be:
#!/usr/bin/env ruby
require 'makecal'
class UI < Thor
include Thor::Actions
# def self.source_root
# File.dirname(__FILE__)
# end
#
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
Thor's documentation on this method is actually wrong and incomplete. It documents that it returns the "contents of the command" (which I assume means the standard output), but it, by defualt, does nothing.
But, you can, apparently, use the :capture option to get what you want:
unless options[:pretend]
config[:capture] ? `#{command}` : system("#{command}")
end
So, try doing
puts run("ls ~", :capture => true)
And see if that does it.

Resources