rest-man 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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