rest-man 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/multi-matrix-test.yml +35 -0
  3. data/.github/workflows/single-matrix-test.yml +27 -0
  4. data/.gitignore +13 -0
  5. data/.mailmap +10 -0
  6. data/.rspec +2 -0
  7. data/.rubocop +2 -0
  8. data/.rubocop-disables.yml +386 -0
  9. data/.rubocop.yml +8 -0
  10. data/AUTHORS +106 -0
  11. data/CHANGELOG.md +7 -0
  12. data/Gemfile +11 -0
  13. data/LICENSE +21 -0
  14. data/README.md +843 -0
  15. data/Rakefile +140 -0
  16. data/exe/restman +92 -0
  17. data/lib/rest-man.rb +2 -0
  18. data/lib/rest_man.rb +2 -0
  19. data/lib/restman/abstract_response.rb +252 -0
  20. data/lib/restman/exceptions.rb +238 -0
  21. data/lib/restman/params_array.rb +72 -0
  22. data/lib/restman/payload.rb +234 -0
  23. data/lib/restman/platform.rb +49 -0
  24. data/lib/restman/raw_response.rb +49 -0
  25. data/lib/restman/request.rb +859 -0
  26. data/lib/restman/resource.rb +178 -0
  27. data/lib/restman/response.rb +90 -0
  28. data/lib/restman/utils.rb +274 -0
  29. data/lib/restman/version.rb +8 -0
  30. data/lib/restman/windows/root_certs.rb +105 -0
  31. data/lib/restman/windows.rb +8 -0
  32. data/lib/restman.rb +183 -0
  33. data/matrixeval.yml +73 -0
  34. data/rest-man.gemspec +41 -0
  35. data/spec/ISS.jpg +0 -0
  36. data/spec/cassettes/request_httpbin_with_basic_auth.yml +83 -0
  37. data/spec/cassettes/request_httpbin_with_cookies.yml +49 -0
  38. data/spec/cassettes/request_httpbin_with_cookies_2.yml +94 -0
  39. data/spec/cassettes/request_httpbin_with_cookies_3.yml +49 -0
  40. data/spec/cassettes/request_httpbin_with_encoding_deflate.yml +45 -0
  41. data/spec/cassettes/request_httpbin_with_encoding_deflate_and_accept_headers.yml +44 -0
  42. data/spec/cassettes/request_httpbin_with_encoding_gzip.yml +45 -0
  43. data/spec/cassettes/request_httpbin_with_encoding_gzip_and_accept_headers.yml +44 -0
  44. data/spec/cassettes/request_httpbin_with_user_agent.yml +44 -0
  45. data/spec/cassettes/request_mozilla_org.yml +151 -0
  46. data/spec/cassettes/request_mozilla_org_callback_returns_true.yml +178 -0
  47. data/spec/cassettes/request_mozilla_org_with_system_cert.yml +152 -0
  48. data/spec/cassettes/request_mozilla_org_with_system_cert_and_callback.yml +151 -0
  49. data/spec/helpers.rb +54 -0
  50. data/spec/integration/_lib.rb +1 -0
  51. data/spec/integration/capath_digicert/README +8 -0
  52. data/spec/integration/capath_digicert/ce5e74ef.0 +1 -0
  53. data/spec/integration/capath_digicert/digicert.crt +20 -0
  54. data/spec/integration/capath_digicert/update +1 -0
  55. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  56. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  57. data/spec/integration/capath_verisign/README +8 -0
  58. data/spec/integration/capath_verisign/verisign.crt +14 -0
  59. data/spec/integration/certs/digicert.crt +20 -0
  60. data/spec/integration/certs/verisign.crt +14 -0
  61. data/spec/integration/httpbin_spec.rb +137 -0
  62. data/spec/integration/integration_spec.rb +118 -0
  63. data/spec/integration/request_spec.rb +134 -0
  64. data/spec/spec_helper.rb +40 -0
  65. data/spec/unit/_lib.rb +1 -0
  66. data/spec/unit/abstract_response_spec.rb +145 -0
  67. data/spec/unit/exceptions_spec.rb +108 -0
  68. data/spec/unit/params_array_spec.rb +36 -0
  69. data/spec/unit/payload_spec.rb +295 -0
  70. data/spec/unit/raw_response_spec.rb +22 -0
  71. data/spec/unit/request2_spec.rb +54 -0
  72. data/spec/unit/request_spec.rb +1205 -0
  73. data/spec/unit/resource_spec.rb +134 -0
  74. data/spec/unit/response_spec.rb +252 -0
  75. data/spec/unit/restclient_spec.rb +80 -0
  76. data/spec/unit/utils_spec.rb +147 -0
  77. data/spec/unit/windows/root_certs_spec.rb +22 -0
  78. metadata +336 -0
@@ -0,0 +1,20 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
3
+ ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
4
+ b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
5
+ MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
6
+ b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
7
+ ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
8
+ 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
9
+ IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
10
+ VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
11
+ 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
12
+ jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
13
+ AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
14
+ A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
15
+ U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
16
+ N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
17
+ o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
18
+ 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
19
+ rqXRfboQnoZsG4q5WTP468SQvvG5
20
+ -----END CERTIFICATE-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
3
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
4
+ cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
5
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
6
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
7
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
8
+ ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
9
+ BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
10
+ I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
11
+ CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
12
+ lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
13
+ AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
14
+ -----END CERTIFICATE-----
@@ -0,0 +1,137 @@
1
+ require_relative '_lib'
2
+ require 'json'
3
+
4
+ require 'zlib'
5
+
6
+ describe RestMan::Request do
7
+
8
+ def default_httpbin_url
9
+ # add a hack to work around java/jruby bug
10
+ # java.lang.RuntimeException: Could not generate DH keypair with backtrace
11
+ # Also (2017-04-09) Travis Jruby versions have a broken CA keystore
12
+ if ENV['TRAVIS_RUBY_VERSION'] =~ /\Ajruby-/
13
+ 'http://httpbin.org/'
14
+ else
15
+ 'https://httpbin.org/'
16
+ end
17
+ end
18
+
19
+ def httpbin(suffix='')
20
+ url = ENV.fetch('HTTPBIN_URL', default_httpbin_url)
21
+ unless url.end_with?('/')
22
+ url += '/'
23
+ end
24
+
25
+ url + suffix
26
+ end
27
+
28
+ def execute_httpbin(suffix, opts={})
29
+ opts = {url: httpbin(suffix)}.merge(opts)
30
+ RestMan::Request.execute(opts)
31
+ end
32
+
33
+ def execute_httpbin_json(suffix, opts={})
34
+ JSON.parse(execute_httpbin(suffix, opts))
35
+ end
36
+
37
+ describe '.execute' do
38
+ it 'sends a user agent' do
39
+ VCR.use_cassette("request_httpbin_with_user_agent") do
40
+ data = execute_httpbin_json('user-agent', method: :get)
41
+ expect(data['user-agent']).to match(/rest-man/)
42
+ end
43
+ end
44
+
45
+ it 'receives cookies on 302' do
46
+ VCR.use_cassette("request_httpbin_with_cookies") do
47
+ expect {
48
+ execute_httpbin('cookies/set?foo=bar', method: :get, max_redirects: 0)
49
+ }.to raise_error(RestMan::Found) { |ex|
50
+ expect(ex.http_code).to eq 302
51
+ expect(ex.response.cookies['foo']).to eq 'bar'
52
+ }
53
+ end
54
+ end
55
+
56
+ it 'passes along cookies through 302' do
57
+ VCR.use_cassette("request_httpbin_with_cookies_2") do
58
+ data = execute_httpbin_json('cookies/set?foo=bar', method: :get)
59
+ expect(data).to have_key('cookies')
60
+ expect(data['cookies']['foo']).to eq 'bar'
61
+ end
62
+ end
63
+
64
+ it 'handles quote wrapped cookies' do
65
+ VCR.use_cassette("request_httpbin_with_cookies_3") do
66
+ expect {
67
+ execute_httpbin('cookies/set?foo=' + CGI.escape('"bar:baz"'),
68
+ method: :get, max_redirects: 0)
69
+ }.to raise_error(RestMan::Found) { |ex|
70
+ expect(ex.http_code).to eq 302
71
+ expect(ex.response.cookies['foo']).to eq '"bar:baz"'
72
+ }
73
+ end
74
+ end
75
+
76
+ it 'sends basic auth' do
77
+ VCR.use_cassette("request_httpbin_with_basic_auth") do
78
+ user = 'user'
79
+ pass = 'pass'
80
+
81
+ data = execute_httpbin_json("basic-auth/#{user}/#{pass}", method: :get, user: user, password: pass)
82
+ expect(data).to eq({'authenticated' => true, 'user' => user})
83
+
84
+ expect {
85
+ execute_httpbin_json("basic-auth/#{user}/#{pass}", method: :get, user: user, password: 'badpass')
86
+ }.to raise_error(RestMan::Unauthorized) { |ex|
87
+ expect(ex.http_code).to eq 401
88
+ }
89
+ end
90
+ end
91
+
92
+ it 'handles gzipped/deflated responses' do
93
+ [['gzip', 'gzipped'], ['deflate', 'deflated']].each do |encoding, var|
94
+ VCR.use_cassette("request_httpbin_with_encoding_#{encoding}") do
95
+ raw = execute_httpbin(encoding, method: :get)
96
+
97
+ begin
98
+ data = JSON.parse(raw)
99
+ rescue StandardError
100
+ puts "Failed to parse: " + raw.inspect
101
+ raise
102
+ end
103
+
104
+ expect(data['method']).to eq 'GET'
105
+ expect(data.fetch(var)).to be true
106
+ end
107
+ end
108
+ end
109
+
110
+ it 'does not uncompress response when accept-encoding is set' do
111
+ VCR.use_cassette("request_httpbin_with_encoding_gzip_and_accept_headers") do
112
+ # == gzip ==
113
+ raw = execute_httpbin('gzip', method: :get, headers: {accept_encoding: 'gzip, deflate'})
114
+
115
+ # check for gzip magic number
116
+ expect(raw.body).to start_with("\x1F\x8B".b)
117
+
118
+ decoded = Zlib::GzipReader.new(StringIO.new(raw.body)).read
119
+ parsed = JSON.parse(decoded)
120
+
121
+ expect(parsed['method']).to eq 'GET'
122
+ expect(parsed.fetch('gzipped')).to be true
123
+ end
124
+
125
+ VCR.use_cassette("request_httpbin_with_encoding_deflate_and_accept_headers") do
126
+ # == delate ==
127
+ raw = execute_httpbin('deflate', method: :get, headers: {accept_encoding: 'gzip, deflate'})
128
+
129
+ decoded = Zlib::Inflate.new.inflate(raw.body)
130
+ parsed = JSON.parse(decoded)
131
+
132
+ expect(parsed['method']).to eq 'GET'
133
+ expect(parsed.fetch('deflated')).to be true
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,118 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '_lib'
3
+ require 'base64'
4
+
5
+ describe RestMan do
6
+
7
+ it "a simple request" do
8
+ body = 'abc'
9
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 200)
10
+ response = RestMan.get "www.example.com"
11
+ expect(response.code).to eq 200
12
+ expect(response.body).to eq body
13
+ end
14
+
15
+ it "a 404" do
16
+ body = "Ho hai ! I'm not here !"
17
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
18
+ begin
19
+ RestMan.get "www.example.com"
20
+ raise
21
+ rescue RestMan::ResourceNotFound => e
22
+ expect(e.http_code).to eq 404
23
+ expect(e.response.code).to eq 404
24
+ expect(e.response.body).to eq body
25
+ expect(e.http_body).to eq body
26
+ end
27
+ end
28
+
29
+ describe 'charset parsing' do
30
+ it 'handles utf-8' do
31
+ body = "λ".force_encoding('ASCII-8BIT')
32
+ stub_request(:get, "www.example.com").to_return(
33
+ :body => body, :status => 200, :headers => {
34
+ 'Content-Type' => 'text/plain; charset=UTF-8'
35
+ })
36
+ response = RestMan.get "www.example.com"
37
+ expect(response.encoding).to eq Encoding::UTF_8
38
+ expect(response.valid_encoding?).to eq true
39
+ end
40
+
41
+ it 'handles windows-1252' do
42
+ body = "\xff".force_encoding('ASCII-8BIT')
43
+ stub_request(:get, "www.example.com").to_return(
44
+ :body => body, :status => 200, :headers => {
45
+ 'Content-Type' => 'text/plain; charset=windows-1252'
46
+ })
47
+ response = RestMan.get "www.example.com"
48
+ expect(response.encoding).to eq Encoding::WINDOWS_1252
49
+ expect(response.encode('utf-8')).to eq "ÿ"
50
+ expect(response.valid_encoding?).to eq true
51
+ end
52
+
53
+ it 'handles binary' do
54
+ body = "\xfe".force_encoding('ASCII-8BIT')
55
+ stub_request(:get, "www.example.com").to_return(
56
+ :body => body, :status => 200, :headers => {
57
+ 'Content-Type' => 'application/octet-stream; charset=binary'
58
+ })
59
+ response = RestMan.get "www.example.com"
60
+ expect(response.encoding).to eq Encoding::BINARY
61
+ expect {
62
+ response.encode('utf-8')
63
+ }.to raise_error(Encoding::UndefinedConversionError)
64
+ expect(response.valid_encoding?).to eq true
65
+ end
66
+
67
+ it 'handles euc-jp' do
68
+ body = "\xA4\xA2\xA4\xA4\xA4\xA6\xA4\xA8\xA4\xAA".
69
+ force_encoding(Encoding::BINARY)
70
+ body_utf8 = 'あいうえお'
71
+ expect(body_utf8.encoding).to eq Encoding::UTF_8
72
+
73
+ stub_request(:get, 'www.example.com').to_return(
74
+ :body => body, :status => 200, :headers => {
75
+ 'Content-Type' => 'text/plain; charset=EUC-JP'
76
+ })
77
+ response = RestMan.get 'www.example.com'
78
+ expect(response.encoding).to eq Encoding::EUC_JP
79
+ expect(response.valid_encoding?).to eq true
80
+ expect(response.length).to eq 5
81
+ expect(response.encode('utf-8')).to eq body_utf8
82
+ end
83
+
84
+ it 'defaults to the default encoding' do
85
+ stub_request(:get, 'www.example.com').to_return(
86
+ body: 'abc', status: 200, headers: {
87
+ 'Content-Type' => 'text/plain'
88
+ })
89
+
90
+ response = RestMan.get 'www.example.com'
91
+ # expect(response.encoding).to eq Encoding.default_external
92
+ expect(response.encoding).to eq Encoding::UTF_8
93
+ end
94
+
95
+ it 'handles invalid encoding' do
96
+ stub_request(:get, 'www.example.com').to_return(
97
+ body: 'abc', status: 200, headers: {
98
+ 'Content-Type' => 'text; charset=plain'
99
+ })
100
+
101
+ response = RestMan.get 'www.example.com'
102
+ # expect(response.encoding).to eq Encoding.default_external
103
+ expect(response.encoding).to eq Encoding::UTF_8
104
+ end
105
+
106
+ it 'leaves images as binary' do
107
+ gif = Base64.strict_decode64('R0lGODlhAQABAAAAADs=')
108
+
109
+ stub_request(:get, 'www.example.com').to_return(
110
+ body: gif, status: 200, headers: {
111
+ 'Content-Type' => 'image/gif'
112
+ })
113
+
114
+ response = RestMan.get 'www.example.com'
115
+ expect(response.encoding).to eq Encoding::BINARY
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,134 @@
1
+ require_relative '_lib'
2
+
3
+ describe RestMan::Request do
4
+
5
+ describe "ssl verification" do
6
+ it "is successful with the correct ca_file" do
7
+ VCR.use_cassette('request_mozilla_org') do
8
+ request = RestMan::Request.new(
9
+ :method => :get,
10
+ :url => 'https://www.mozilla.org',
11
+ :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "digicert.crt")
12
+ )
13
+ expect { request.execute }.to_not raise_error
14
+ end
15
+ end
16
+
17
+ it "is successful with the correct ca_path" do
18
+ VCR.use_cassette('request_mozilla_org') do
19
+ request = RestMan::Request.new(
20
+ :method => :get,
21
+ :url => 'https://www.mozilla.org',
22
+ :ssl_ca_path => File.join(File.dirname(__FILE__), "capath_digicert")
23
+ )
24
+ expect { request.execute }.to_not raise_error
25
+ end
26
+ end
27
+
28
+ it "is unsuccessful with an incorrect ca_file", :unless => RestMan::Platform.mac_mri? do
29
+ VCR.use_cassette('request_mozilla_org_with_incorrect_ca_file') do
30
+ request = RestMan::Request.new(
31
+ :method => :get,
32
+ :url => 'https://www.mozilla.org',
33
+ :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt")
34
+ )
35
+ expect { request.execute }.to raise_error(OpenSSL::SSL::SSLError)
36
+ end
37
+ end
38
+
39
+ # On OS X, this test fails since Apple has patched OpenSSL to always fall
40
+ # back on the system CA store.
41
+ it "is unsuccessful with an incorrect ca_path", :unless => RestMan::Platform.mac_mri? do
42
+ VCR.use_cassette('request_mozilla_org_with_incorrect_ca_path') do
43
+ request = RestMan::Request.new(
44
+ :method => :get,
45
+ :url => 'https://www.mozilla.org',
46
+ :ssl_ca_path => File.join(File.dirname(__FILE__), "capath_verisign")
47
+ )
48
+ expect { request.execute }.to raise_error(OpenSSL::SSL::SSLError)
49
+ end
50
+ end
51
+
52
+ it "is successful using the default system cert store" do
53
+ VCR.use_cassette('request_mozilla_org_with_system_cert') do
54
+ request = RestMan::Request.new(
55
+ :method => :get,
56
+ :url => 'https://www.mozilla.org',
57
+ :verify_ssl => true,
58
+ )
59
+ expect {request.execute }.to_not raise_error
60
+ end
61
+ end
62
+
63
+
64
+ # verify_callback is not works well with VCR
65
+ # it "executes the verify_callback", focus: true do
66
+ # ran_callback = false
67
+ # request = RestMan::Request.new(
68
+ # :method => :get,
69
+ # :url => 'https://www.mozilla.org',
70
+ # :verify_ssl => true,
71
+ # :ssl_verify_callback => lambda { |preverify_ok, store_ctx|
72
+ # ran_callback = true
73
+ # preverify_ok
74
+ # },
75
+ # )
76
+ # expect {request.execute }.to_not raise_error
77
+ # expect(ran_callback).to eq(true)
78
+ # end
79
+
80
+ it "fails verification when the callback returns false",
81
+ :unless => RestMan::Platform.mac_mri? do
82
+ VCR.use_cassette('request_mozilla_org_callback_returns_false') do
83
+ request = RestMan::Request.new(
84
+ :method => :get,
85
+ :url => 'https://www.mozilla.org',
86
+ :verify_ssl => true,
87
+ :ssl_verify_callback => lambda { |preverify_ok, store_ctx| false },
88
+ )
89
+ expect { request.execute }.to raise_error(OpenSSL::SSL::SSLError)
90
+ end
91
+ end
92
+
93
+ it "succeeds verification when the callback returns true",
94
+ :unless => RestMan::Platform.mac_mri? do
95
+ VCR.use_cassette('request_mozilla_org_callback_returns_true') do
96
+ request = RestMan::Request.new(
97
+ :method => :get,
98
+ :url => 'https://www.mozilla.org',
99
+ :verify_ssl => true,
100
+ :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt"),
101
+ :ssl_verify_callback => lambda { |preverify_ok, store_ctx| true },
102
+ )
103
+ expect { request.execute }.to_not raise_error
104
+ end
105
+ end
106
+ end
107
+
108
+ describe "timeouts" do
109
+ it "raises OpenTimeout when it hits an open timeout" do
110
+ allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(Net::OpenTimeout.new)
111
+
112
+ request = RestMan::Request.new(
113
+ :method => :get,
114
+ :url => 'http://www.mozilla.org',
115
+ :open_timeout => 1e-10,
116
+ )
117
+ expect { request.execute }.to(
118
+ raise_error(RestMan::Exceptions::OpenTimeout))
119
+ end
120
+
121
+ it "raises ReadTimeout when it hits a read timeout via :read_timeout" do
122
+ allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(Net::ReadTimeout.new)
123
+
124
+ request = RestMan::Request.new(
125
+ :method => :get,
126
+ :url => 'https://www.mozilla.org',
127
+ :read_timeout => 1e-10,
128
+ )
129
+ expect { request.execute }.to(
130
+ raise_error(RestMan::Exceptions::ReadTimeout))
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,40 @@
1
+ require 'webmock/rspec'
2
+ require 'rest-man'
3
+
4
+ require_relative './helpers'
5
+ require 'byebug' unless RUBY_PLATFORM == 'java'
6
+ require 'ruby-debug' if RUBY_PLATFORM == 'java'
7
+ require 'vcr'
8
+
9
+ VCR.configure do |config|
10
+ config.default_cassette_options = { match_requests_on: %i[uri method] }
11
+ config.cassette_library_dir = 'spec/cassettes'
12
+ config.hook_into :webmock
13
+ end
14
+
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ config.raise_errors_for_deprecations!
18
+
19
+ # Run specs in random order to surface order dependencies. If you find an
20
+ # order dependency and want to debug it, you can fix the order by providing
21
+ # the seed, which is printed after each run.
22
+ # --seed 1234
23
+ config.order = 'random'
24
+
25
+ # always run with ruby warnings enabled
26
+ # TODO: figure out why this is so obscenely noisy (rspec bug?)
27
+ # config.warnings = true
28
+
29
+ # add helpers
30
+ config.include Helpers, :include_helpers
31
+
32
+ config.filter_run_when_matching :focus
33
+
34
+ config.mock_with :rspec do |mocks|
35
+ mocks.yield_receiver_to_any_instance_implementation_blocks = true
36
+ end
37
+ end
38
+
39
+ # always run with ruby warnings enabled (see above)
40
+ $VERBOSE = true
data/spec/unit/_lib.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative '../spec_helper'
@@ -0,0 +1,145 @@
1
+ require_relative '_lib'
2
+
3
+ describe RestMan::AbstractResponse, :include_helpers do
4
+
5
+ # Sample class implementing AbstractResponse used for testing.
6
+ class MyAbstractResponse
7
+
8
+ include RestMan::AbstractResponse
9
+
10
+ attr_accessor :size
11
+
12
+ def initialize(net_http_res, request)
13
+ response_set_vars(net_http_res, request, Time.now - 1)
14
+ end
15
+
16
+ end
17
+
18
+ before do
19
+ @net_http_res = res_double()
20
+ @request = request_double(url: 'http://example.com', method: 'get')
21
+ @response = MyAbstractResponse.new(@net_http_res, @request)
22
+ end
23
+
24
+ it "fetches the numeric response code" do
25
+ expect(@net_http_res).to receive(:code).and_return('200')
26
+ expect(@response.code).to eq 200
27
+ end
28
+
29
+ it "has a nice description" do
30
+ expect(@net_http_res).to receive(:to_hash).and_return({'Content-Type' => ['application/pdf']})
31
+ expect(@net_http_res).to receive(:code).and_return('200')
32
+ expect(@response.description).to eq "200 OK | application/pdf bytes\n"
33
+ end
34
+
35
+ describe '.beautify_headers' do
36
+ it "beautifies the headers by turning the keys to symbols" do
37
+ h = RestMan::AbstractResponse.beautify_headers('content-type' => [ 'x' ])
38
+ expect(h.keys.first).to eq :content_type
39
+ end
40
+
41
+ it "beautifies the headers by turning the values to strings instead of one-element arrays" do
42
+ h = RestMan::AbstractResponse.beautify_headers('x' => [ 'text/html' ] )
43
+ expect(h.values.first).to eq 'text/html'
44
+ end
45
+
46
+ it 'joins multiple header values by comma' do
47
+ expect(RestMan::AbstractResponse.beautify_headers(
48
+ {'My-Header' => ['one', 'two']}
49
+ )).to eq({:my_header => 'one, two'})
50
+ end
51
+
52
+ it 'leaves set-cookie headers as array' do
53
+ expect(RestMan::AbstractResponse.beautify_headers(
54
+ {'Set-Cookie' => ['cookie1=foo', 'cookie2=bar']}
55
+ )).to eq({:set_cookie => ['cookie1=foo', 'cookie2=bar']})
56
+ end
57
+ end
58
+
59
+ it "fetches the headers" do
60
+ expect(@net_http_res).to receive(:to_hash).and_return('content-type' => [ 'text/html' ])
61
+ expect(@response.headers).to eq({ :content_type => 'text/html' })
62
+ end
63
+
64
+ it "extracts cookies from response headers" do
65
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
66
+ expect(@response.cookies).to eq({ 'session_id' => '1' })
67
+ end
68
+
69
+ it "extract strange cookies" do
70
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
71
+ expect(@response.headers).to eq({:set_cookie => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/']})
72
+ expect(@response.cookies).to eq({ 'session_id' => 'ZJ/HQVH6YE+rVkTpn0zvTQ==' })
73
+ end
74
+
75
+ it "doesn't escape cookies" do
76
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
77
+ expect(@response.cookies).to eq({ 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' })
78
+ end
79
+
80
+ describe '.cookie_jar' do
81
+ it 'extracts cookies into cookie jar' do
82
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
83
+ expect(@response.cookie_jar).to be_a HTTP::CookieJar
84
+
85
+ cookie = @response.cookie_jar.cookies.first
86
+ expect(cookie.domain).to eq 'example.com'
87
+ expect(cookie.name).to eq 'session_id'
88
+ expect(cookie.value).to eq '1'
89
+ expect(cookie.path).to eq '/'
90
+ end
91
+
92
+ it 'handles cookies when URI scheme is implicit' do
93
+ net_http_res = double('net http response')
94
+ expect(net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
95
+ request = double('request', url: 'example.com', uri: URI.parse('http://example.com'),
96
+ method: 'get', cookie_jar: HTTP::CookieJar.new, redirection_history: nil)
97
+ response = MyAbstractResponse.new(net_http_res, request)
98
+ expect(response.cookie_jar).to be_a HTTP::CookieJar
99
+
100
+ cookie = response.cookie_jar.cookies.first
101
+ expect(cookie.domain).to eq 'example.com'
102
+ expect(cookie.name).to eq 'session_id'
103
+ expect(cookie.value).to eq '1'
104
+ expect(cookie.path).to eq '/'
105
+ end
106
+ end
107
+
108
+ it "can access the net http result directly" do
109
+ expect(@response.net_http_res).to eq @net_http_res
110
+ end
111
+
112
+ describe "#return!" do
113
+ it "should return the response itself on 200-codes" do
114
+ expect(@net_http_res).to receive(:code).and_return('200')
115
+ expect(@response.return!).to be_equal(@response)
116
+ end
117
+
118
+ it "should raise RequestFailed on unknown codes" do
119
+ expect(@net_http_res).to receive(:code).and_return('1000')
120
+ expect { @response.return! }.to raise_error RestMan::RequestFailed
121
+ end
122
+
123
+ it "should raise an error on a redirection after non-GET/HEAD requests" do
124
+ expect(@net_http_res).to receive(:code).and_return('301')
125
+ expect(@request).to receive(:method).and_return('put')
126
+ expect(@response).not_to receive(:follow_redirection)
127
+ expect { @response.return! }.to raise_error RestMan::RequestFailed
128
+ end
129
+
130
+ it "should follow 302 redirect" do
131
+ expect(@net_http_res).to receive(:code).and_return('302')
132
+ expect(@response).to receive(:check_max_redirects).and_return('fake-check')
133
+ expect(@response).to receive(:follow_redirection).and_return('fake-redirection')
134
+ expect(@response.return!).to eq 'fake-redirection'
135
+ end
136
+
137
+ it "should gracefully handle 302 redirect with no location header" do
138
+ @net_http_res = res_double(code: 302)
139
+ @request = request_double()
140
+ @response = MyAbstractResponse.new(@net_http_res, @request)
141
+ expect(@response).to receive(:check_max_redirects).and_return('fake-check')
142
+ expect { @response.return! }.to raise_error RestMan::Found
143
+ end
144
+ end
145
+ end