http_monkey 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ require "net/http"
2
+ require "rack"
3
+ require "httpi"
4
+
5
+ require "http_monkey/version"
6
+ require "http_monkey/entry_point"
7
+ require "http_monkey/configuration"
8
+ require "http_monkey/configuration/behaviours"
9
+ require "http_monkey/configuration/middlewares"
10
+ require "http_monkey/client"
11
+ require "http_monkey/client/http_request"
12
+ require "http_monkey/client/environment"
13
+ require "http_monkey/client/environment_builder"
14
+ require "http_monkey/middlewares"
15
+
16
+ module HttpMonkey
17
+
18
+ def self.at(url)
19
+ default_client.at(url)
20
+ end
21
+
22
+ def self.configure(&block)
23
+ default_client.configure(&block)
24
+ end
25
+
26
+ def self.build(&block)
27
+ HttpMonkey::Client.new.configure(&block)
28
+ end
29
+
30
+ protected
31
+
32
+ def self.default_client
33
+ @@default_client ||= build do
34
+ net_adapter :net_http
35
+ behaviours do
36
+ # Follow redirects
37
+ on([301, 302, 303, 307]) do |client, request, response|
38
+ if (location = response.headers["location"])
39
+ request.url = location
40
+ client.http_request(:get, request)
41
+ else
42
+ raise "HTTP status #{response.code} not supported (or location not found)"
43
+ end
44
+ end
45
+ # By default, always return response
46
+ on_unknown do |client, request, response|
47
+ response
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ HTTPI.log = false
56
+
57
+ # This monkey patch is to avoid a bug from httpi (1.1.1) on ruby 1.8.7
58
+ # that raises "undefined method `use_ssl=' for Net::HTTP"
59
+ if RUBY_VERSION =~ /1\.8/
60
+ class Net::HTTPSession
61
+ def use_ssl=(flag)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Client::Environment do
4
+
5
+ subject { HttpMonkey::Client::Environment }
6
+
7
+ describe "#accessor" do
8
+ it "gets like Hash" do
9
+ env = subject.new(:test => "value")
10
+ env[:test].must_equal("value")
11
+ end
12
+ it "sets like Hash" do
13
+ env = subject.new(:test => "value")
14
+ env[:test] = "owned"
15
+ env[:test].must_equal("owned")
16
+ end
17
+ end
18
+
19
+ it "#http_headers" do
20
+ env = subject.new("HTTP_CONTENT_TYPE" => "text/html",
21
+ "HTTP_X_CUSTOM" => "custom")
22
+
23
+ env.http_headers.must_be_instance_of(Hash)
24
+ env.http_headers["Content-Type"].must_equal("text/html")
25
+ env.http_headers["X-Custom"].must_equal("custom")
26
+ end
27
+
28
+ end
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Client do
4
+
5
+ subject do
6
+ HttpMonkey::Client.new
7
+ end
8
+
9
+ it "#at" do
10
+ subject.at("http://server.com").must_be_instance_of(HttpMonkey::EntryPoint)
11
+ end
12
+
13
+ describe "default values" do
14
+ it "#net_adapter" do
15
+ subject.net_adapter.must_equal(:net_http)
16
+ end
17
+ end
18
+
19
+ describe "#configure" do
20
+ it "#net_adapter" do
21
+ subject.configure do
22
+ net_adapter(:curb)
23
+ end
24
+ subject.net_adapter.must_equal(:curb)
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,34 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Configuration::Behaviours do
4
+
5
+ subject { HttpMonkey::Configuration::Behaviours.new }
6
+
7
+ describe "#on" do
8
+ it "support Integer code" do
9
+ subject.on(1) { "test int" }
10
+
11
+ subject.find(1).call.must_equal("test int")
12
+ subject.find(2).must_be_nil
13
+ end
14
+ it "support Range code" do
15
+ subject.on(90...100) { "test range" }
16
+
17
+ subject.find(90).call.must_equal("test range")
18
+ subject.find(99).call.must_equal("test range")
19
+ subject.find(100).must_be_nil
20
+ end
21
+ it "support Array code (or anything that respond_to include?)" do
22
+ subject.on([2,3]) { "test array" }
23
+
24
+ subject.find(2).call.must_equal("test array")
25
+ subject.find(3).call.must_equal("test array")
26
+ subject.find(1).must_be_nil
27
+ end
28
+ end
29
+
30
+ it "#on_unknown" do
31
+ subject.on_unknown { "ok" }
32
+ subject.unknown_behaviour.call.must_equal("ok")
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Configuration::Middlewares do
4
+
5
+ subject { HttpMonkey::Configuration::Middlewares.new }
6
+
7
+ class MiddleBase
8
+ def initialize(app, args = {}, &block)
9
+ @app = app
10
+ @args = args
11
+ @block = block
12
+ end
13
+ def call(env)
14
+ env[:args] = @args
15
+ env[:block] = @block
16
+ env[:app] ||= []
17
+ env[:app] << "before #{self.class.to_s}"
18
+ result = @app.call(env)
19
+ env[:app] << "after #{self.class.to_s}"
20
+ result
21
+ end
22
+ end
23
+
24
+ class Middle1 < MiddleBase; end
25
+ class Middle2 < MiddleBase; end
26
+
27
+ let(:app) do
28
+ Proc.new do |env|
29
+ env[:app] << "app"
30
+ "OK"
31
+ end
32
+ end
33
+
34
+ let(:env) do
35
+ {:app => []}
36
+ end
37
+
38
+ describe "#use" do
39
+ it "delegate args" do
40
+ subject.use Middle1, :arg1 =>'arg1', :arg2 => "arg2"
41
+ subject.execute(app, env)
42
+ env[:args].must_equal({:arg1 =>'arg1', :arg2 => "arg2"})
43
+ end
44
+ it "delegate block" do
45
+ subject.use Middle1 do
46
+ "Middle1 block"
47
+ end
48
+ subject.execute(app, env)
49
+ env[:block].call.must_equal("Middle1 block")
50
+ end
51
+ end
52
+
53
+ describe "#execute" do
54
+ it "without chain" do
55
+ result = subject.execute(app, env)
56
+ result.must_equal("OK")
57
+ end
58
+ it "chain of one" do
59
+ subject.use Middle1
60
+ subject.execute(app, env)
61
+
62
+ env[:app].must_equal(["before Middle1", "app", "after Middle1"])
63
+ end
64
+ it "chain of many (maintain order)" do
65
+ subject.use Middle1
66
+ subject.use Middle2
67
+ subject.execute(app, env)
68
+
69
+ env[:app].must_equal(["before Middle1", "before Middle2", "app", "after Middle2", "after Middle1"])
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,64 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Configuration do
4
+
5
+ subject do
6
+ HttpMonkey::Configuration.new
7
+ end
8
+
9
+ describe "#clone" do
10
+ it "#behaviours" do
11
+ clone = subject.clone
12
+ subject.behaviours.wont_be_same_as(clone.behaviours)
13
+ end
14
+ it "#middlewares" do
15
+ clone = subject.clone
16
+ subject.middlewares.wont_be_same_as(clone.middlewares)
17
+ end
18
+ end
19
+
20
+ describe "#net_adapter" do
21
+ it "returns value" do
22
+ subject.net_adapter.must_equal(:net_http)
23
+ end
24
+ it "sets value" do
25
+ subject.net_adapter(:curb)
26
+ subject.net_adapter.must_equal(:curb)
27
+ end
28
+ end
29
+
30
+ describe "#behaviours" do
31
+ it "respond_to" do
32
+ subject.must_respond_to(:behaviours)
33
+ end
34
+ it "returns value" do
35
+ subject.behaviours.must_be_instance_of(HttpMonkey::Configuration::Behaviours)
36
+ end
37
+ it "sets value using block" do
38
+ flag = "out block"
39
+ subject.behaviours do
40
+ self.must_be_instance_of(HttpMonkey::Configuration::Behaviours)
41
+ flag = "inside block"
42
+ end
43
+ flag.must_equal("inside block")
44
+ end
45
+ end
46
+
47
+ describe "#middlewares" do
48
+ it "respond_to" do
49
+ subject.must_respond_to(:middlewares)
50
+ end
51
+ it "returns value" do
52
+ subject.middlewares.must_be_instance_of(HttpMonkey::Configuration::Middlewares)
53
+ end
54
+ it "sets value using block" do
55
+ flag = "out block"
56
+ subject.middlewares do
57
+ self.must_be_instance_of(HttpMonkey::Configuration::Middlewares)
58
+ flag = "inside block"
59
+ end
60
+ flag.must_equal("inside block")
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,135 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::EntryPoint do
4
+
5
+ TEST_URL = "https://www.google.com.br"
6
+
7
+ before do
8
+ @mock_client = stub("client")
9
+ end
10
+
11
+ subject do
12
+ HttpMonkey::EntryPoint.new(@mock_client, TEST_URL)
13
+ end
14
+
15
+ def expects_request_on(client, method, body, &block)
16
+ client.expects(:http_request).with do |method, request|
17
+ if block.respond_to?(:call)
18
+ block_assertion = block.call(method, request)
19
+ else
20
+ block_assertion = true
21
+ end
22
+
23
+ request_url = request.url.select(:scheme, :host).join("://")
24
+
25
+ body_assertion = false
26
+ if body.nil?
27
+ body_assertion = request.body.must_be_nil
28
+ elsif body.is_a?(String)
29
+ body_assertion = request.body.must_equal(body)
30
+ elsif body.is_a?(Regexp)
31
+ body_assertion = request.body.must_match(body)
32
+ end
33
+
34
+ block_assertion && \
35
+ method.must_equal(method) && \
36
+ request_url.must_equal(TEST_URL) && \
37
+ request.must_be_instance_of(HTTPI::Request) && \
38
+ body_assertion
39
+ end
40
+ end
41
+
42
+ it "#get" do
43
+ expects_request_on(@mock_client, :get, nil)
44
+ subject.get
45
+ end
46
+
47
+ it "#get with parameters" do
48
+ expects_request_on(@mock_client, :get, nil) do |method, request|
49
+ request.url.query.must_match(/(p1=param1&p2=param2)|(p2=param2&p1=param1)/)
50
+ end
51
+
52
+ subject.get(:p1 => "param1", :p2 => "param2")
53
+ end
54
+
55
+ it "#post with parameters" do
56
+ expects_request_on(@mock_client, :post, /(b1=param1&b2=param2)|(b2=param2&b1=param1)/)
57
+ subject.post(:b1 => "param1", :b2 => "param2")
58
+ end
59
+
60
+ it "#put with parameters" do
61
+ expects_request_on(@mock_client, :put, /(p1=param1&p2=param2)|(p2=param2&p1=param1)/)
62
+ subject.put(:p1 => "param1", :p2 => "param2")
63
+ end
64
+
65
+ it "#delete" do
66
+ expects_request_on(@mock_client, :delete, nil)
67
+ subject.delete
68
+ end
69
+
70
+ it "HTTP requests should accept configuration block" do
71
+ block = lambda { raise "Not to be raised" }
72
+ @mock_client.expects(:clone).returns(@mock_client)
73
+ @mock_client.expects(:configure).returns(@mock_client)
74
+ expects_request_on(@mock_client, :get, nil)
75
+
76
+ subject.get(&block)
77
+ end
78
+
79
+ describe "FluentInterface -_-" do
80
+
81
+ it "#with_header" do
82
+ expects_request_on(@mock_client, :get, nil) do |method, request|
83
+ request.headers.must_equal({"Content-Type" => "text/html", "X-Custom" => "sample"})
84
+ end
85
+ subject.with_header("Content-Type" => "text/html").
86
+ with_header("X-Custom" => "sample").
87
+ get
88
+ end
89
+
90
+ it "#with_headers" do
91
+ expects_request_on(@mock_client, :get, nil) do |method, request|
92
+ request.headers.must_equal({"Content-Type" => "text/html", "X-Custom" => "sample"})
93
+ end
94
+ subject.with_headers("Content-Type" => "text/html",
95
+ "X-Custom" => "sample").get
96
+ end
97
+
98
+ it "#set_cookie" do
99
+ expects_request_on(@mock_client, :get, nil) do |method, request|
100
+ request.headers["Cookie"].must_equal("name=value;name2=value2")
101
+ end
102
+ subject.set_cookie("name=value;name2=value2").get
103
+ end
104
+
105
+ it "#basic_auth" do
106
+ expects_request_on(@mock_client, :get, nil) do |method, request|
107
+ request.auth.type.must_equal(:basic) && \
108
+ request.auth.credentials.must_equal(["mad", "max"])
109
+ end
110
+ subject.basic_auth("mad", "max").get
111
+ end
112
+
113
+ it "#digest_auth" do
114
+ expects_request_on(@mock_client, :get, nil) do |method, request|
115
+ request.auth.type.must_equal(:digest) && \
116
+ request.auth.credentials.must_equal(["mad", "max"])
117
+ end
118
+ subject.digest_auth("mad", "max").get
119
+ end
120
+
121
+ it "#yield_request" do
122
+ expects_request_on(@mock_client, :get, nil) do |method, request|
123
+ request.proxy.to_s.must_equal("http://proxy.com") && \
124
+ request.open_timeout.must_equal(30) && \
125
+ request.read_timeout.must_equal(15)
126
+ end
127
+ subject.yield_request do |req|
128
+ req.proxy = "http://proxy.com"
129
+ req.open_timeout = 30
130
+ req.read_timeout = 15
131
+ end.get
132
+ end
133
+ end
134
+
135
+ end
@@ -0,0 +1,26 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey do
4
+
5
+ subject { HttpMonkey }
6
+
7
+ it "#at" do
8
+ subject.at("http://google.com.br").must_be_instance_of(HttpMonkey::EntryPoint)
9
+ end
10
+
11
+ it "#configure" do
12
+ flag = "out block"
13
+ subject.configure do
14
+ self.must_be_instance_of(HttpMonkey::Configuration)
15
+ flag = "inside block"
16
+ end
17
+ flag.must_equal("inside block")
18
+ end
19
+
20
+ describe "#build" do
21
+ it "wont be same client" do
22
+ subject.build.wont_be_same_as(HttpMonkey.default_client)
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,32 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Middlewares::DefaultHeaders do
4
+
5
+ before do
6
+ @mock_app = stub("app", :call => "stubbed")
7
+ end
8
+
9
+ let(:fake_env) { Hash.new }
10
+ subject { HttpMonkey::Middlewares::DefaultHeaders }
11
+
12
+ it "always call app" do
13
+ @mock_app.expects(:call).with(fake_env)
14
+ subject.new(@mock_app).call(fake_env)
15
+ end
16
+
17
+ it "sets header" do
18
+ middle = subject.new(@mock_app, {"X-Custom" => "value"})
19
+ middle.call(fake_env)
20
+
21
+ fake_env["HTTP_X_CUSTOM"].must_equal("value")
22
+ end
23
+
24
+ it "not overwrite header" do
25
+ fake_env["HTTP_X_CUSTOM"] = "request"
26
+ middle = subject.new(@mock_app, {"X-Custom" => "value"})
27
+ middle.call(fake_env)
28
+
29
+ fake_env["HTTP_X_CUSTOM"].must_equal("request")
30
+ end
31
+
32
+ end
@@ -0,0 +1,73 @@
1
+ require "test_helper"
2
+
3
+ # Inspiration from https://github.com/djanowski/mock-server
4
+ class IntegrationServer
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def start(host = "localhost", port = 4000)
11
+ Thread.new do
12
+ silence_output do # comment this if you want information
13
+ Rack::Server.start(
14
+ :app => @app,
15
+ :server => 'webrick',
16
+ :environment => :none,
17
+ :daemonize => false,
18
+ :Host => host,
19
+ :Port => port
20
+ )
21
+ end
22
+ end
23
+ wait_for_service(host, port)
24
+ true
25
+ end
26
+
27
+ protected
28
+
29
+ # quick and dirty
30
+ def silence_output
31
+ $stdout = File.new('/dev/null', 'w')
32
+ $stderr = File.new('/dev/null', 'w')
33
+ yield
34
+ ensure
35
+ $stdout = STDOUT
36
+ $stderr = STDERR
37
+ end
38
+
39
+ def listening?(host, port)
40
+ begin
41
+ socket = TCPSocket.new(host, port)
42
+ socket.close unless socket.nil?
43
+ true
44
+ rescue Errno::ECONNREFUSED,
45
+ Errno::EBADF, # Windows
46
+ Errno::EADDRNOTAVAIL # Windows
47
+ false
48
+ end
49
+ end
50
+
51
+ def wait_for_service(host, port, timeout = 5)
52
+ start_time = Time.now
53
+
54
+ until listening?(host, port)
55
+ if timeout && (Time.now > (start_time + timeout))
56
+ raise SocketError.new("Socket did not open within #{timeout} seconds")
57
+ end
58
+ end
59
+
60
+ true
61
+ end
62
+
63
+ end
64
+
65
+ class IntegrationServer::InspectEnv
66
+ def initialize(app)
67
+ @app = app
68
+ end
69
+ def call(env)
70
+ puts "-> #{env.inspect}"
71
+ @app.call(env)
72
+ end
73
+ end
@@ -0,0 +1,58 @@
1
+ require "integration/server"
2
+
3
+ VerbsApp = Rack::Builder.new do
4
+ #use IntegrationServer::InspectEnv # i can see clear now the rain is gone ...
5
+ map "/" do
6
+ run lambda { |env|
7
+ env['rack.input'] = env['rack.input'].read if env['rack.input'].respond_to?(:read)
8
+ body = YAML.dump(env)
9
+ [200, {"Content-Type" => "text/plain", "Content-Length" => body.size.to_s}, [body]]
10
+ }
11
+ end
12
+ end
13
+
14
+ IntegrationServer.new(VerbsApp).start
15
+
16
+ describe "Integration Specs - Verbs" do
17
+
18
+ describe "#get" do
19
+ it "no parameter" do
20
+ response = HttpMonkey.at("http://localhost:4000").get
21
+ server_env = YAML.load(response.body)
22
+
23
+ server_env["REQUEST_METHOD"].must_equal("GET")
24
+ server_env["QUERY_STRING"].must_be_empty
25
+ end
26
+ it "with parameters" do
27
+ response = HttpMonkey.at("http://localhost:4000").get(:q => "query")
28
+ server_env = YAML.load(response.body)
29
+
30
+ server_env["REQUEST_METHOD"].must_equal("GET")
31
+ server_env["QUERY_STRING"].must_equal("q=query")
32
+ end
33
+ end
34
+
35
+ it "#post" do
36
+ response = HttpMonkey.at("http://localhost:4000").post(:var => "post_var")
37
+ server_env = YAML.load(response.body)
38
+
39
+ server_env["REQUEST_METHOD"].must_equal("POST")
40
+ server_env["rack.input"].must_equal("var=post_var")
41
+ end
42
+
43
+ it "#put" do
44
+ response = HttpMonkey.at("http://localhost:4000").put(:var => "put_var")
45
+ server_env = YAML.load(response.body)
46
+
47
+ server_env["REQUEST_METHOD"].must_equal("PUT")
48
+ server_env["rack.input"].must_equal("var=put_var")
49
+ end
50
+
51
+ it "#delete" do
52
+ response = HttpMonkey.at("http://localhost:4000").delete
53
+ server_env = YAML.load(response.body)
54
+
55
+ server_env["REQUEST_METHOD"].must_equal("DELETE")
56
+ end
57
+
58
+ end
@@ -0,0 +1,9 @@
1
+ require "http_monkey"
2
+
3
+ require "minitest/autorun"
4
+ require "minitest/reporters"
5
+
6
+ require "mocha"
7
+
8
+ MiniTest::Unit.runner = MiniTest::SuiteRunner.new
9
+ MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new