nginx_test_helper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rvmrc
7
+ .project
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nginx_test_helper.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Wandenberg Peixoto
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # NginxTestHelper
2
+
3
+ A collection of helper methods to test your nginx module.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'nginx_test_helper'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install nginx_test_helper
18
+
19
+ ## Usage
20
+
21
+ Create a module called NginxConfiguration with two class methods:
22
+ `default_configuration`, which should return a hash with the default configuration values, and `template_configuration` which should return the Nginx configuration template.
23
+
24
+ You can use the command bellow to generate this file
25
+
26
+ $ nginx_test_helper init
27
+
28
+ The init command also create a example_spec.rb to show how to use the main methods:
29
+
30
+ ### nginx_test_configuration
31
+
32
+ Starts the server with the given configuration and template, stop it and return the `stderr` and `error log` to be possible to check some condition.
33
+
34
+ nginx_test_configuration({:unknown_value => 0}).should include('unknown directive "unknown_directive"')
35
+
36
+ ### nginx_run_server
37
+
38
+ Starts the server, execute the given block inside a `Timeout block` and stop the server.
39
+
40
+ nginx_run_server({:return_code => 422}) do
41
+ uri = URI.parse("http://#{nginx_host}:#{nginx_port}/")
42
+ response = Net::HTTP.get_response(uri)
43
+ response.code.should eql("422")
44
+ end
45
+
46
+ You can customize the timeout value, default 5 seconds, using the second parameter of `nginx_run_server` method.
47
+
48
+ nginx_run_server({}, {:timeout => 1}) do
49
+ sleep 2
50
+ end
51
+
52
+ ### start_server / stop_server
53
+
54
+ If you want to start the server and run many test cases with the same configuration you can use `start_server / stop_server` methods.
55
+
56
+ before(:all) do
57
+ configuration = {} # Your configuration hash
58
+ @config = NginxTestHelper::Config.new("example_config_id", configuration)
59
+ start_server(@config)
60
+ end
61
+
62
+ after(:all) do
63
+ stop_server(@config)
64
+ end
65
+
66
+ ### delete_config_and_log_files
67
+
68
+ You can use this method to delete the files created by configuration.
69
+ One usecase is call it after the test, if it has passed, like:
70
+
71
+ RSpec.configure do |config|
72
+ config.after(:each) do
73
+ NginxTestHelper::Config.delete_config_and_log_files(config_id) if has_passed?
74
+ end
75
+ end
76
+
77
+ ## Environment variables
78
+
79
+ Some default values can be overwriten by environment variables.
80
+ Check the list bellow:
81
+
82
+ 1. NGINX_EXEC - set which nginx executable to be used on tests, default: '/usr/local/nginx/sbin/nginx'
83
+ 2. NGINX_HOST - set the host returned by `nginx_host` method, default: '127.0.0.1'
84
+ 3. NGINX_PORT - set the port returned by `nginx_port` method, default: 9990
85
+ 4. NGINX_WORKERS - set the number of workers returned by `nginx_workers` method, default: 1
86
+ 5. NGINX_TESTS_TMP_DIR - set the dir where temporary files, logs and configuration files, will be stored, default: '/tmp/nginx_tests'
87
+
88
+ ## Easter eggs
89
+
90
+ ### configuration_template
91
+
92
+ You can set a key named `configuration_template` on your configuration with a template different from the one on `template_configuration` method to be used when writing configuration file.
93
+
94
+ ### disable_start_stop_server
95
+
96
+ You can set a key named `disable_start_stop_server` on your configuration with `true` value to avoid the start and stop server steps. This can be useful when debugging how a test is failing.
97
+
98
+ ### write_directive
99
+
100
+ You can use the method `write_directive` on your configuration template to be easier to deal with null values.
101
+
102
+ write_directive("directive_name", value)
103
+
104
+ with value = nil results in
105
+ #directive_name "";
106
+
107
+ with value != nil, 10 as example, results in
108
+ directive_name "10";
109
+
110
+ There is a third optional parameter which is used as comment to directive.
111
+
112
+ write_directive("directive_name", 10, "directive comment")
113
+
114
+ #directive comment
115
+ directive_name "10";
116
+
117
+ ## Matchers
118
+
119
+ There are two available mathers.
120
+
121
+ ### be_in_the_interval
122
+
123
+ To check if the value is in a range, `>= min` and `<= max`
124
+
125
+ 10.5.should be_in_the_interval(10.3, 10.6) # true
126
+ 10.5.should be_in_the_interval(10.6, 10.8) # false
127
+
128
+ ### match_the_pattern
129
+
130
+ To check if the value match the given pattern
131
+
132
+ "foo".should match_the_pattern(/O/i) # true
133
+ "foo".should match_the_pattern(/A/i) # false
134
+
135
+ ## Contributing
136
+
137
+ 1. Fork it
138
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
139
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
140
+ 4. Push to the branch (`git push origin my-new-feature`)
141
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ desc "Run all examples"
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.rspec_opts = %w[--color --format documentation]
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ end
10
+
11
+ task :default => [:spec]
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'nginx_test_helper'
5
+
6
+ NginxTestHelper::CommandLineTool.new.process ARGV
@@ -0,0 +1,59 @@
1
+ module NginxTestHelper
2
+ class CommandLineTool
3
+ def cwd
4
+ File.expand_path(File.join(File.dirname(__FILE__), '../../'))
5
+ end
6
+
7
+ def expand(*paths)
8
+ File.expand_path(File.join(*paths))
9
+ end
10
+
11
+ def template_path(filepath)
12
+ expand(cwd, File.join("templates", filepath))
13
+ end
14
+
15
+ def dest_path(filepath)
16
+ expand(Dir.pwd, filepath)
17
+ end
18
+
19
+ def copy_unless_exists(relative_path, dest_path = nil)
20
+ unless File.exist?(dest_path(relative_path))
21
+ FileUtils.copy(template_path(relative_path), dest_path(dest_path || relative_path))
22
+ end
23
+ end
24
+
25
+ def process(argv)
26
+ if argv[0] == 'init'
27
+ require 'fileutils'
28
+
29
+ FileUtils.makedirs(dest_path('spec'))
30
+
31
+ copy_unless_exists('spec/nginx_configuration.rb')
32
+ copy_unless_exists('spec/example_spec.rb')
33
+ copy_unless_exists('spec/example2_spec.rb')
34
+
35
+ write_mode = 'w'
36
+ if File.exist?(dest_path('spec/spec_helper.rb'))
37
+ load dest_path('spec/spec_helper.rb')
38
+ write_mode = 'a'
39
+ end
40
+
41
+ unless Object.const_defined?('NginxConfiguration') && write_mode == 'a'
42
+ File.open(dest_path('spec/spec_helper.rb'), write_mode) do |f|
43
+ f.write("\nrequire File.expand_path('nginx_configuration', File.dirname(__FILE__))")
44
+ end
45
+ end
46
+
47
+ File.open(template_path('INSTALL'), 'r').each_line do |line|
48
+ puts line
49
+ end
50
+ elsif argv[0] == "license"
51
+ puts File.new(expand(cwd, "LICENSE")).read
52
+ else
53
+ puts "unknown command #{argv}"
54
+ puts "Usage: nginx_test_helper init"
55
+ puts " license"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,70 @@
1
+ require "erb"
2
+
3
+ module NginxTestHelper
4
+ class Config
5
+
6
+ include NginxTestHelper::EnvMethods
7
+
8
+ attr_reader :config_id, :configuration
9
+
10
+ def initialize(config_id, configuration)
11
+ @config_id = config_id
12
+ @configuration = NginxConfiguration.default_configuration.merge(configuration).merge(Config.files_and_dirs(config_id))
13
+
14
+ Config.create_dirs
15
+ create_configuration_file
16
+ end
17
+
18
+ def create_configuration_file
19
+ configuration_template = configuration.delete(:configuration_template)
20
+ template = ERB.new configuration_template || NginxConfiguration.template_configuration
21
+ File.open(configuration[:configuration_filename], 'w') {|f| f.write(template.result(get_binding)) }
22
+ end
23
+
24
+ def respond_to?(m)
25
+ super(m) || configuration.has_key?(m.to_sym)
26
+ end
27
+
28
+ def method_missing(m, *args, &block)
29
+ raise NameError, "undefined local variable, method or configuration '#{m}'" unless configuration.has_key?(m.to_sym)
30
+ configuration[m.to_sym]
31
+ end
32
+
33
+ def write_directive(name, value, comment=nil)
34
+ directive = []
35
+ directive << %(##{comment}) unless comment.nil?
36
+ directive << %(#{"#" if value.nil?}#{name} "#{value}";)
37
+ directive.join("\n")
38
+ end
39
+
40
+ def get_binding
41
+ binding
42
+ end
43
+
44
+ class << self
45
+ def create_dirs
46
+ FileUtils.mkdir_p("#{nginx_tests_tmp_dir}/logs") unless File.directory?("#{nginx_tests_tmp_dir}/logs")
47
+ FileUtils.mkdir_p("#{nginx_tests_tmp_dir}/client_body_temp") unless File.directory?("#{nginx_tests_tmp_dir}/client_body_temp")
48
+ end
49
+
50
+ def files_and_dirs(config_id)
51
+ {
52
+ :configuration_filename => File.expand_path("#{nginx_tests_tmp_dir}/#{config_id}.conf"),
53
+ :pid_file => File.expand_path("#{nginx_tests_tmp_dir}/nginx.pid"),
54
+ :access_log => File.expand_path("#{nginx_tests_tmp_dir}/logs/access-#{config_id}.log"),
55
+ :error_log => File.expand_path("#{nginx_tests_tmp_dir}/logs/error-#{config_id}.log"),
56
+ :client_body_temp => File.expand_path("#{nginx_tests_tmp_dir}/client_body_temp")
57
+ }
58
+ end
59
+
60
+ def delete_config_and_log_files(config_id)
61
+ items = files_and_dirs(config_id)
62
+
63
+ File.delete(items[:configuration_filename]) if File.exist?(items[:configuration_filename])
64
+ File.delete(items[:access_log]) if File.exist?(items[:access_log])
65
+ File.delete(items[:error_log]) if File.exist?(items[:error_log])
66
+ FileUtils.rm_rf(items[:client_body_temp]) if File.exist?(items[:client_body_temp])
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,35 @@
1
+ module NginxTestHelper
2
+ module EnvMethods
3
+ module ClassMethods
4
+ def nginx_address
5
+ "http://#{nginx_host}:#{nginx_port}"
6
+ end
7
+
8
+ def nginx_executable
9
+ ENV['NGINX_EXEC'].nil? ? "/usr/local/nginx/sbin/nginx" : ENV['NGINX_EXEC']
10
+ end
11
+
12
+ def nginx_host
13
+ ENV['NGINX_HOST'].nil? ? "127.0.0.1" : ENV['NGINX_HOST']
14
+ end
15
+
16
+ def nginx_port
17
+ ENV['NGINX_PORT'].nil? ? "9990" : ENV['NGINX_PORT']
18
+ end
19
+
20
+ def nginx_workers
21
+ ENV['NGINX_WORKERS'].nil? ? "1" : ENV['NGINX_WORKERS']
22
+ end
23
+
24
+ def nginx_tests_tmp_dir
25
+ ENV['NGINX_TESTS_TMP_DIR'].nil? ? "/tmp/nginx_tests" : ENV['NGINX_TESTS_TMP_DIR']
26
+ end
27
+ end
28
+
29
+ include ClassMethods
30
+
31
+ def self.included(base)
32
+ base.extend ClassMethods
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ if defined?(RSpec)
2
+ RSpec.configure do |config|
3
+ config.include NginxTestHelper
4
+ end
5
+
6
+ RSpec::Matchers.define :be_in_the_interval do |min, max|
7
+ match do |actual|
8
+ (actual >= min) && (actual <= max)
9
+ end
10
+
11
+ failure_message_for_should do |actual|
12
+ "expected that #{actual} would be in the interval from #{min} to #{max}"
13
+ end
14
+
15
+ failure_message_for_should_not do |actual|
16
+ "expected that #{actual} would not be in the interval from #{min} to #{max}"
17
+ end
18
+
19
+ description do
20
+ "be in the interval from #{min} to #{max}"
21
+ end
22
+ end
23
+
24
+ RSpec::Matchers.define :match_the_pattern do |pattern|
25
+ match do |actual|
26
+ actual.match(pattern)
27
+ end
28
+
29
+ failure_message_for_should do |actual|
30
+ "expected that '#{actual}' would match the pattern '#{pattern.inspect}'"
31
+ end
32
+
33
+ failure_message_for_should_not do |actual|
34
+ "expected that '#{actual}' would not match the pattern '#{pattern.inspect}'"
35
+ end
36
+
37
+ description do
38
+ "match the pattern '#{pattern.inspect}'"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module NginxTestHelper
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,118 @@
1
+ require "nginx_test_helper/version"
2
+ require "nginx_test_helper/env_methods"
3
+ require "nginx_test_helper/config"
4
+ require "nginx_test_helper/rspec_utils"
5
+ require "nginx_test_helper/command_line_tool"
6
+ require "popen4"
7
+ require 'timeout'
8
+
9
+ module NginxTestHelper
10
+ include NginxTestHelper::EnvMethods
11
+
12
+ def nginx_run_server(configuration={}, options={}, &block)
13
+ config = Config.new(config_id, configuration)
14
+ start_server(config)
15
+ Timeout::timeout(options[:timeout] || 5) do
16
+ block.call(config)
17
+ end
18
+ ensure
19
+ stop_server(config) unless config.nil?
20
+ end
21
+
22
+ def nginx_test_configuration(configuration={})
23
+ config = Config.new(config_id, configuration)
24
+ stderr_msg = start_server(config)
25
+ stop_server(config)
26
+ "#{stderr_msg}\n#{File.read(config.error_log) if File.exists?(config.error_log)}"
27
+ end
28
+
29
+ def open_socket(host, port)
30
+ TCPSocket.open(host, port)
31
+ end
32
+
33
+ def get_in_socket(url, socket, wait_for=nil)
34
+ socket.print("GET #{url} HTTP/1.0\r\n\r\n")
35
+ read_response_on_socket(socket, wait_for)
36
+ end
37
+
38
+ def post_in_socket(url, body, socket, wait_for=nil)
39
+ socket.print("POST #{url} HTTP/1.0\r\nContent-Length: #{body.size}\r\n\r\n#{body}")
40
+ read_response_on_socket(socket, wait_for)
41
+ end
42
+
43
+ def read_response_on_socket(socket, wait_for=nil)
44
+ response ||= socket.readpartial(1)
45
+ while (tmp = socket.read_nonblock(256))
46
+ response += tmp
47
+ end
48
+ rescue Errno::EAGAIN => e
49
+ headers, body = (response || "").split("\r\n\r\n", 2)
50
+ if !wait_for.nil? && (body.nil? || body.empty? || !body.include?(wait_for))
51
+ IO.select([socket])
52
+ retry
53
+ end
54
+ ensure
55
+ fail("Any response") if response.nil?
56
+ headers, body = response.split("\r\n\r\n", 2)
57
+ return headers, body
58
+ end
59
+
60
+ def time_diff_milli(start, finish)
61
+ ((finish - start) * 1000.0).to_i
62
+ end
63
+
64
+ def time_diff_sec(start, finish)
65
+ (finish - start).to_i
66
+ end
67
+
68
+ def headers
69
+ {'accept' => 'text/html'}
70
+ end
71
+
72
+ def start_server(config)
73
+ error_message = ""
74
+ unless config.configuration[:disable_start_stop_server]
75
+ status = POpen4::popen4("#{ config.nginx_executable } -c #{ config.configuration_filename }") do |stdout, stderr, stdin, pid|
76
+ error_message = stderr.read.strip unless stderr.eof
77
+ return error_message unless error_message.nil?
78
+ end
79
+ fail("Server doesn't started - #{error_message}") unless status.exitstatus == 0
80
+ end
81
+ error_message
82
+ end
83
+
84
+ def stop_server(config)
85
+ error_message = ""
86
+ unless config.configuration[:disable_start_stop_server]
87
+ status = POpen4::popen4("#{ config.nginx_executable } -c #{ config.configuration_filename } -s stop") do |stdout, stderr, stdin, pid|
88
+ error_message = stderr.read.strip unless stderr.eof
89
+ return error_message unless error_message.nil?
90
+ end
91
+ fail("Server doesn't stoped - #{error_message}") unless status.exitstatus == 0
92
+ end
93
+ error_message
94
+ end
95
+
96
+ private
97
+ def config_id
98
+ if self.respond_to?(:example) && !self.example.nil? &&
99
+ self.example.respond_to?(:metadata) && !self.example.metadata.nil? &&
100
+ !self.example.metadata[:location].nil?
101
+ (self.example.metadata[:location].split('/') - [".", "spec"]).join('_').gsub(/[\.\:]/, '_')
102
+ elsif self.respond_to?('method_name')
103
+ self.method_name
104
+ else
105
+ self.__name__
106
+ end
107
+ end
108
+
109
+ def has_passed?
110
+ if self.respond_to?(:example) && !self.example.nil? && self.example.instance_variable_defined?(:@exception)
111
+ self.example.exception.nil?
112
+ elsif !@test_passed.nil?
113
+ @test_passed
114
+ else
115
+ @passed
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/nginx_test_helper/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Wandenberg Peixoto"]
6
+ gem.email = ["wandenberg@gmail.com"]
7
+ gem.description = %q{A collection of helper methods to test your nginx module.}
8
+ gem.summary = %q{A collection of helper methods to test your nginx module.}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "nginx_test_helper"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = NginxTestHelper::VERSION
17
+
18
+ gem.add_dependency "popen4"
19
+
20
+ gem.add_development_dependency(%q<rspec>, [">= 2.10.0"])
21
+ gem.add_development_dependency(%q<debugger>, [">= 1.1.3"])
22
+ gem.add_development_dependency(%q<simplecov>, [">= 0.0.1"]) if RUBY_VERSION > "1.9.0"
23
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe NginxTestHelper::CommandLineTool do
4
+ before do
5
+ $stdout.stub!(:puts)
6
+ Dir.stub!(:pwd).and_return('tmp')
7
+ FileUtils.mkdir_p('tmp/spec')
8
+ end
9
+
10
+ after do
11
+ FileUtils.rm_rf('tmp')
12
+ end
13
+
14
+ it "should list available options" do
15
+ $stdout.should_receive(:puts).with(/init/)
16
+ $stdout.should_receive(:puts).with(/license/)
17
+ NginxTestHelper::CommandLineTool.new.process []
18
+ end
19
+
20
+ it "should list the gem license" do
21
+ $stdout.should_receive(:puts).with(/MIT License/)
22
+ NginxTestHelper::CommandLineTool.new.process ["license"]
23
+ end
24
+
25
+ it "should create example files showing how to use the gem" do
26
+ NginxTestHelper::CommandLineTool.new.process ["init"]
27
+ File.exists?('tmp/spec/nginx_configuration.rb').should be_true
28
+ File.exists?('tmp/spec/example_spec.rb').should be_true
29
+ File.exists?('tmp/spec/spec_helper.rb').should be_true
30
+
31
+ File.read('tmp/spec/nginx_configuration.rb').should eql(File.read('templates/spec/nginx_configuration.rb'))
32
+ File.read('tmp/spec/example_spec.rb').should eql(File.read('templates/spec/example_spec.rb'))
33
+ File.read('tmp/spec/spec_helper.rb').should include('nginx_configuration')
34
+ end
35
+
36
+ it "should include require call on spec_helper if NgincConfiguration is not defined" do
37
+ Object.stub!(:const_defined?).with('NginxConfiguration').and_return(false)
38
+ File.open('tmp/spec/spec_helper.rb', 'w') { |f| f.write("#spec_helper content") }
39
+ NginxTestHelper::CommandLineTool.new.process ["init"]
40
+ File.read('tmp/spec/spec_helper.rb').should eql("#spec_helper content\nrequire File.expand_path('nginx_configuration', File.dirname(__FILE__))")
41
+ end
42
+
43
+ it "should print INSTALL file content" do
44
+ $stdout.should_receive(:puts).with(/Nginx Test Helper has been installed with example specs./)
45
+ NginxTestHelper::CommandLineTool.new.process ["init"]
46
+ end
47
+ end