Passing options and parameters to Test/Unit via Rake::TestTask - ruby

So I've been trying to figure this out, and the best solution I can came up with his global variables - but that seems so dirty and 1974 - Am I missing a feature of Rake/ Test::Unit?
I have a Rake file in which I'm running tests:
Rake::TestTask.new(:test) do |t|
t.test_files = FileList['test_*.rb']
end
and test_1.rb has something like this:
require "test/unit"
class TestStuff < Test::Unit::TestCase
def setup
#thingy = Thing.New(parameter1, parameter2)
end
def test_this_thing
#thing.do()
end
end
My problem is, Thing.new() requires arguments, and those arguments are specific to my environment. In reality, I'm running Selenium-WebDriver, and I want to pass in a browser type, and a url, etc... sometimes I want ff, othertimes I want chrome... sometimes this URL, sometimes that... depending on the day, etc.
The simplest thing seems to do something like:
#all that rake stuff
$parameter1 = x
$parameter2 = y
and then make Thing.new() look up my global vars:
#thingy = Thing.New($parameter1, $parameter2)
This seems sloppy.. and it just doesn't feel right to me. I'm still trying to get this 'test harness' up and running, and want to do it right the first time. That's why I chose Rake, based on a lot of other feedback.
Keep in mind, I'll probably have 100's of tests, ultimately, and they'll all need to do get this information... I thought Rake was good at making sure all of this was easy, but it doesn't seem to be.
Where did I go wrong?

I have used YAML files to store my configuration (browser config, environments including URLs, etc).
You can also use an environmental variable to define simple configurations. You can access environmental variables via ENV['foobar'] in Ruby.
So, for example, my browser call might look like this inside my setup method:
driver = Selenium::WebDriver.for (ENV['SWD_BROWSER'] || "firefox").to_sym
and in my Rake file (or in the shell console) define the environmental variable to use.

Related

Setting path to custom .irbrc using IRB.conf

I want to invoke irb dynamically from my Ruby program, but have it not to load the default ~/.irbrc, but a file ./custom_irbrc instead. I can do it like this:
require 'irb'
ENV['IRBRC'] = './custom_irbrc'
IRB.setup(nil)
# My configurations follow here
IRB.conf[...]=...
IRB.start
I wonder whether I can set my custom irbrc also via .conf instead of polluting the environment. I didn't find a really comprehensive description of the possible conf-settings, but from what I found, I tried as educated guess:
IRB.conf[:IRB_RC] = './custom_irbrc'
IRB.conf[:RC] = './custom_irbrc'
but neither one seems to have any effect.
The desired effect can be achieved, although by using an undocumented feature, and there is no guarantee that it will be available in future Ruby versions too:
IRB.conf[:RC_NAME_GENERATOR] = proc { './custom_irbrc' }
This has to be done before IRB.setup is called.

How do I programmatically set a content_security_policy?

I'm configuring the Content Security Policy for our Rails 5.2 app. I need to whitelist some domains in our CSP. I'd like to put the list of domains elsewhere so I can reference them in other places in the application, then generate the CSP headers programmatically from that list.
Looking at the source code for the Content Security Policy configuration mechanisms in Rails 5, it looks like there's some magic metaprogramming going on, so it's not clear to me how to accomplish what I need to do. It looks like the functions I need to call to set headers might be picky about how exactly they want to be called. In particular, it's not clear to me if I can pass them arrays or safely call them multiple times, or if they do some metaprogramming magic that only works if the domains are passed in as individual function arguments.
Can I pass in an array to the header I want to set, like this?
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
csp.child_src whitelisted_domains
end
end
Or can I call the same function multiple times, like this?
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
whitelisted_domains.each {|domain| csp.child_src domain}
end
end
If neither of those will work, what's the best way of accomplishing what I want to do?
From what I can tell from sourcecode and documentation, it takes an array. From the edgeguides at rails, posting following
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
...
end
and the sourcecode, using *sources as param; it believe it takes any number of arguments, meaning you could do something along the lines of;
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
csp.child_src(*whitelisted_domains)
end
end
https://blog.sqreen.io/integrating-content-security-policy-into-your-rails-applications-4f883eed8f45/
https://edgeguides.rubyonrails.org/security.html#content-security-policy
Sourcecode of define_method for each directive
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/content_security_policy.rb#L151
(note: None of this has been tested in a Rails app, simple looking guides and source code of Rails)

Is it better idea to save elements in yml file?

I'm writing automated tests using Selenium WebDriver with Ruby. So, I'm thinking to keep elements in another file and actual code in another file. And for Ruby, I found yaml gem which allows to store data and access it. Hence I stored elements in lib.yml and test code in test.rb as following:
lib/lib.yml
homepage:
frame: 'mainPage'
email: 'loginPage-email'
password: 'loginPage-password'
login_button: 'btnLogin'
tests/test.rb
require 'selenium-webdriver'
require 'yaml'
driver = Selenium::WebDriver.for :firefox
driver.get 'http://www.abc.com'
config = YAML.load_file('./lib/lib.yml')
driver.switch_to.frame(config['homepage']['frame'])
email = driver.find_element(:id, config['homepage']['email'])
password = driver.find_element(:id, config['homepage']['password'])
email.clear
email.send_keys 'abc#gmail.com'
password.clear
password.send_keys 'password'
driver.find_element(:id, config['homepage']['login_button']).click
driver.quit
This way maintenance becomes easier. I just want to make sure if doing so is a good way or not. I'm asking because I'm trying this first time and don't know what difficulties I'll run into if I choose this for larger project.
I know, using Page object model, we can achieve same thing. But I don't know about Page object. So should I avoid using yml gem and directly go for page object gem?
Also, can someone explain how using yml will not be good idea(if it's not)?
Note:
In above code, config['homepage']['something'] is repetitive code. I'll write method to avoid repetition for that.
Yeah this definitely is useful... It keeps the changes to minimum when there is UI change in future.. You always have just one place to edit... Is there any data you have to pass to your code? How are storing the automation data passed to your test.. The only concern might be you might end up with too many yaml files which could be difficult to keep track...
In your specific case I don't see how this adds much value. Half of the settings (frame, login_button) won't change for your tests, so I suggest leaving them directly in the code where they are used. The html structure is not something that usually changes.
The other two values (email, password) seem like they might change when you want to try out different users (i.e. different cases). If you have one test with several example inputs then I suggest using a more readable solution as Cucumber.
(I'd suggest using capybara anyway for testing browser interaction, as it abstracts away many details of the underlying driver)
Apart from that, yaml is usually the ruby way for storing configuration.
I added one more step: Declared locator (id, name etc) in the yaml itself.
Ex:(yaml)
Declared env.rb which load the environment from yaml files
env.yml:
LOGIN:
UserName: {id: UserName}
Password: {id: Password}
RememberME: {id: RememberMe}
Submit: {xpath: "//input[#value='Log On']"}
Then added "pages\Login.rb"
#Loads all objects from yaml
def get_objects
username=#browser.find_element( $object_array['LOGIN']['UserName'])
password=#browser.find_element( $object_array['LOGIN']['Password'])
remember_me=#browser.find_element( $object_array['LOGIN']['RememberME'])
submit= #browser.find_element($object_array['LOGIN']['Submit'])
end
#Added methods in this class like
def loginas(uname,pass)
username.send_keys uname
password.send_keys pass
remember_me.click
submit.click
end #loginas_siteadmin
Created Tests file Login_tests.rb
lp=LoginPage::new(#browser)
lp.navigate
lp.loginas('SiteAdmin','password123')
This way your scripts and maintainable and most importantly you are free of any other external gem or dependency.

Passing values from Capistrano deploy.rb file to app

In my Capistrano's deploy.rb file, I set up different environments such as server names, ports, etc. I also require the users to send a callback to another server, also defined in the deploy.rb. How do I cleanly pass this value to my app?
Something to this effect:
config/deploy.rb:
set :callback_url, "http://somecallbackurl.com:12345/bla"
app/controllers/myapp.rb:
def get_callback_url
???
end
I'm using Sinatra.
I found a solution, and that is to use the environment variables.
Set it from deploy.rb
run "export CALLBACK_URL=#{callback_url}"
From app:
def get_callback_url
ENV['CALLBACK_URL']
end
I wouldn't say it's the cleanest solution, but it works.
I'd probably recommend using a shared YAML file to store this kind of configuration, and loading it separately. For example, have a file named something like config/settings.yml, containing something like:
:callback_url: "http://somecallbackurl.com:12345/bla"
In config/deploy.rb, you could have:
settings = YAML.load_file('config/settings.yml')
set :callback_url, settings[:callback_url]
And in config/initializers/settings.rb, you could have:
settings = YAML.load_file('config/settings.yml')
CALLBACK_URL = settings[:callback_url]
Finally, in app/controllers/myapp.rb, you would do:
def get_callback_url
CALLBACK_URL
end
Using a shared YAML file is just the first thing I thought of. Another approach would be defining some constants in a ruby file, and requiring that file both in an initializer, and in deploy.rb. The basic idea is that you don't really want your app to depend on your capistrano environment, so you should find a way to separate the shared configuration.

How to create a sandboxed RSpec environment?

Essentially, I want to create a program that will run some untrusted code that defines some method or class, and then run an untrusted rspec spec against it.
I've looked into sandboxing Ruby a bit, and this video from rubyconf was particularly helpful. After looking at several solutions, the two that appear to be the most helpful are rubycop, which essentially does static analysis on the code, and the jruby sandbox (both covered in above video). My instinct tells me that the jruby sandbox is probably safer, but I could well be wrong.
Here's a completely unsafe example of what I want to do:
code = <<-RUBY
class Person
def hey
"hey!"
end
end
RUBY
spec = <<-RUBY
describe Person do
let(:person) { Person.new }
it "says hey" do
person.hey.should == "hey!"
end
end
RUBY
# code and spec will be from user input (unsafe)
eval code
require 'rspec/autorun'
eval spec
Which all works fine, but the code obviously needs to be sandboxed. It will be a matter of minutes before some genius submits system("rm -rf /*"), fork while fork or something equally dangerous.
I made various attempts with the jruby sandbox...
sand = Sandbox::Safe.new
sand.eval("require 'rspec/autorun'")
sand.activate! # lock it down
sand.eval code
puts sand.eval spec
That code throws this exception:
Sandbox::SandboxException: NoMethodError: undefined method `require' for #<RSpec::Core::Configuration:0x7c3cfaab>
This is because RSpec tries to require some stuff after the sandbox has been locked down.
So, I tried to force RSpec to require stuff before the sandbox gets locked down by calling an empty describe:
sand = Sandbox::Safe.new
sand.eval("require 'rspec/autorun'")
sand.eval("describe("") { }")
sand.activate! # lock it down
sand.eval code
sand.eval spec
And I get this:
Sandbox::SandboxException: NameError: uninitialized constant RSpec
Which basically means that RSpec doesn't exist in the sandbox. Which is odd, considering sand.eval("require 'rspec/autorun'") returns true, and that the earlier example actually worked (RSpec's autoloader started to run).
It may be a problem with gems and this particular sandbox though. The sandbox object actually supports a method #require, which is essentially bound to Kernel.require, and therefore can't load gems.
It's starting to look like using this sandbox just might not really be possible with rspec. The main problem is trying to actually load it into the sandbox. I even tried something like this:
require 'rspec'
sand.ref(RSpec) # open access to local rspec
But it wasn't having any of it.
So, my question is two-fold:
Does anyone have any bright ideas on how to get this to work with the jruby sandbox?
If not, how secure is rubycop? Apparently codeschool use it, so it must be pretty well tested... it would be nice to be able to use ruby 1.9 instead of jruby as well.
It looks like the sand box environment isn't loading the bundle/gemset. RVM could be at fault here if you are using a gemset or something.
One might try loading the Bundle again once sand boxed.
I would look at ruby taint modes
$SAFE The security level
0 --> No checks are performed on externally supplied (tainted) data. (default)
1 --> Potentially dangerous operations using tainted data are forbidden.
2 --> Potentially dangerous operations on processes and files are forbidden.
3 --> All newly created objects are considered tainted.
4 --> Modification of global data is forbidden.
I have been trying to figure out a similar problem. I want to use some gems like json and rest-client inside my sandbox after activating it. I tried following.
require "sandbox"
s=Sandbox.safe
s.eval <<-RUBY
require 'bundler'
Bundler.require :sandbox
RUBY
s.activate!
Gemfile.rb
group :sandbox do
platforms :jruby do
gem 'json'
gem 'rest-client'
end
end
This way, I was able to require gems in my sandbox. But, then there were some gem specific issues with sandbox. For eg, I had to add a method initialize_dup to whitelist for safe.rb in jruby-sandbox. RestClient has some problem with Fake File Sytem ALT_SEPARATOR which I am trying to patch. You can try this approach for RSpec and see if everything goes through.

Resources