rs
1require 'test_helper'
2
3class ClientTest < Minitest::Test
4 attr_reader :subject
5
6 def setup
7 @subject = Net::Hippie::Client.new
8 end
9
10 def test_get
11 VCR.use_cassette('get_breaches') do
12 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
13 response = subject.get(uri)
14 refute_nil response
15 assert_equal(283, JSON.parse(response.body).count)
16 end
17 end
18
19 def test_multiple_gets_to_pypi
20 VCR.use_cassette('multiple-gets-to-pypi') do
21 %w{
22 https://pypi.org/pypi/django/1.11.3/json
23 https://pypi.org/pypi/Django/1.11.3/json
24 https://pypi.org/pypi/docutils/0.13.1/json
25 https://pypi.org/pypi/pytz/2019.2/json
26 https://pypi.org/pypi/requests/2.5.3/json
27 }.each do |url|
28 subject = Net::Hippie::Client.new(follow_redirects: 3)
29 response = subject.get(url)
30 refute_nil response
31 assert_equal Net::HTTPOK, response.class
32 assert JSON.parse(response.body)
33 end
34 end
35 end
36
37 def test_does_not_follow_redirect
38 VCR.use_cassette('does_not_follow_redirect') do
39 subject = Net::Hippie::Client.new(follow_redirects: 0)
40 response = subject.get('https://pypi.org/pypi/django/1.11.3/json')
41 refute_nil response
42 assert_kind_of Net::HTTPRedirection, response
43 assert response['location']
44 end
45 end
46
47 def test_does_follow_redirects
48 VCR.use_cassette('does_follow_redirects') do
49 subject = Net::Hippie::Client.new(follow_redirects: 10)
50 response = subject.get('https://pypi.org/pypi/django/1.11.3/json')
51 refute_nil response
52 assert_kind_of Net::HTTPOK, response
53 assert JSON.parse(response.body)
54 end
55 end
56
57 def test_follow_redirects_with_relative_paths
58 VCR.use_cassette('follow_redirects_with_relative_paths') do
59 subject = Net::Hippie::Client.new(follow_redirects: 10)
60 response = subject.get("http://go.microsoft.com/fwlink/?LinkId=329770")
61 refute_nil response
62 assert_kind_of Net::HTTPOK, response
63 assert response.body
64 end
65 end
66
67 def test_get_with_redirects
68 url = 'https://www.example.org/'
69 n = 10
70 WebMock
71 .stub_request(:get, url)
72 .to_return(status: 301, headers: { 'Location' => url })
73 .times(n)
74 .then
75 .to_return(status: 200, body: { success: true }.to_json)
76
77 subject = Net::Hippie::Client.new(follow_redirects: n)
78 response = subject.get(url)
79 refute_nil response
80 assert_equal Net::HTTPOK, response.class
81 assert JSON.parse(response.body)['success']
82 end
83
84 def test_get_root_path
85 VCR.use_cassette('get_root') do
86 uri = URI.parse('https://www.mokhan.ca')
87 response = subject.get(uri, headers: {})
88 refute_nil response
89 assert_equal response.code, "200"
90 assert response.body.include?("<!DOCTYPE html>")
91 end
92 end
93
94 def test_get_with_retry
95 uri = URI.parse('https://www.example.org/api/scim/v2/schemas')
96 WebMock.stub_request(:get, uri.to_s)
97 .to_timeout.then
98 .to_timeout.then
99 .to_timeout.then
100 .to_return(status: 200, body: { 'success' => 'true' }.to_json)
101 response = subject.with_retry(retries: 3) do |client|
102 client.get(uri)
103 end
104 refute_nil response
105 assert_equal Net::HTTPOK, response.class
106 assert_equal JSON.parse(response.body)['success'], 'true'
107 end
108
109 def test_exceeds_retries
110 uri = URI.parse('https://www.example.org/api/scim/v2/schemas')
111 WebMock.stub_request(:get, uri.to_s)
112 .to_timeout.then
113 .to_return(status: 200, body: { 'success' => 'true' }.to_json)
114
115 assert_raises Net::OpenTimeout do
116 subject.with_retry(retries: 0) do |client|
117 client.get(uri)
118 end
119 end
120 end
121
122 def test_get_with_string_uri
123 VCR.use_cassette('get_breaches') do
124 response = subject.get('https://haveibeenpwned.com/api/breaches')
125 refute_nil response
126 assert_equal(283, JSON.parse(response.body).count)
127 end
128 end
129
130 def test_get_with_generic_uri
131 VCR.use_cassette('get_breaches') do
132 uri = URI::Generic.build(host: 'haveibeenpwned.com', scheme: 'https', path: '/api/breaches', port: 443)
133 response = subject.get(uri)
134 refute_nil response
135 assert_equal(283, JSON.parse(response.body).count)
136 end
137 end
138
139 def test_get_with_block_syntax
140 VCR.use_cassette('get_breaches') do
141 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
142 subject.get(uri) do |_request, response|
143 @response = response
144 end
145 refute_nil @response
146 assert_equal(283, JSON.parse(@response.body).count)
147 end
148 end
149
150 def test_get_with_headers
151 headers = { 'Accept' => 'application/vnd.haveibeenpwned.v2+json' }
152 WebMock.stub_request(:get, 'https://haveibeenpwned.com/api/breaches')
153 .with(headers: headers)
154 .to_return(status: 201, body: {}.to_json)
155
156 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
157
158 response = subject.get(uri, headers: headers)
159 refute_nil response
160 assert_equal response.class, Net::HTTPCreated
161 end
162
163 def test_post_with_basic_auth_headers
164 VCR.use_cassette('post_basic_auth') do
165 uri = URI.parse('http://localhost:3000/oauth/tokens')
166 client_id = "79a1c787-5cac-4cc5-b00e-374f5a909ef8"
167 client_password = "za9NeRkm9YbqKDa6GreUmo6V"
168 authorization_code = "NGooUnxYq5f5DHvJqyzDhSft"
169 headers = { 'Authorization' => Net::Hippie.basic_auth(client_id, client_password) }
170 body = { grant_type: 'authorization_code', code: authorization_code }
171 response = subject.post(uri, headers: headers, body: body)
172 refute_nil response
173 json = JSON.parse(response.body, symbolize_names: true)
174 assert_equal('Bearer', json[:token_type])
175 assert(json[:token_type])
176 end
177 end
178
179 def test_get_with_bearer_auth_headers
180 VCR.use_cassette('get_bearer_auth') do
181 uri = URI.parse('http://localhost:3000/oauth/me')
182 token = "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1NDE4NzEyMDksImlhdCI6MTU0MTg2NzYwOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwL21ldGFkYXRhIiwibmJmIjoxNTQxODY3NjA5LCJhdWQiOiI3OWExYzc4Ny01Y2FjLTRjYzUtYjAwZS0zNzRmNWE5MDllZjgiLCJqdGkiOiIzZmZmODNkYi1mMmM3LTRjMDUtYWY4Zi02NWM5ODU1ODUyZjciLCJzdWIiOiIwNDJhZjE5Ny1hOTQxLTRkNTctYjc4Ny00M2IxYmNjZDUwNzAiLCJ0b2tlbl90eXBlIjoiYWNjZXNzIn0.HcwFAgg54RqKEONvHRigAavISuUUmkOA3gz0pkV6UEABCUHGucJCNehnvjpiwe4ZpCF_J6Uen6rfLFQOz7oYe416Du3_lQ3IS3Vc6hTpsT0XZ0bY-BVV_D9-thYFIrT7mNnNoxEs8AhOTBaAjgBammO_097MCwMjTGzAnxm1cmYfBad4yZPJ8HxDqeL769Urc6vz3Ku_M9yUzzfgb6jkwfFlvxmqHOPYWxlDY9uTR2uNr-ZYL5e6J6ZE8rgLkNRqy-jla03z2nFxEuxlSjYbBe60Vcwc4IyKS4QzbKXFXB1v9bKBvJxIUjQPQ7dICQeT9xSXQwDhnBtGUVcGM4njSH0-0rbxE470bGVslmYsChosX0PvRqlo4TMuVr7R5iuwWawZrIB-Dx3kkvhFYhn0jWrEEJkd96nLD-dmg2Tzqa40AE2WqmKtM5jM0LNO9E21l-hPQXAleoKspFIjT6Yd2Om4bJi-0eJB6sNqDuP55rvd5WSjp-ktrrtRirt-9aldCB_0eWP9oFCMJ_Xboq5w0P1W5MXlBv5p6eEdgRjohQyT-dkOvYsZiT9-Y5ggbBMhgtHm1CXZnutN0RE_skZk9PvxV_nUfTji3CHIaUhoJmeI11Tw2tdtOEt58RqkFgK7CZYylG7JqQS6eKpTLB2MrrZtCHY3rrrDaL64l6jYYQI"
183 headers = { 'Authorization' => Net::Hippie.bearer_auth(token) }
184 response = subject.get(uri, headers: headers)
185
186 refute_nil response
187 json = JSON.parse(response.body, symbolize_names: true)
188 assert(json[:sub])
189 assert(json[:exp])
190 assert(json[:iss])
191 assert(json[:nbf])
192 end
193 end
194
195 def test_get_with_body
196 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
197 body = { 'hello' => 'world' }
198 WebMock.stub_request(:get, uri.to_s)
199 .with(body: body.to_json)
200 .to_return(status: 201, body: {}.to_json)
201
202 response = subject.get(uri, body: body)
203
204 refute_nil response
205 assert_equal response.class, Net::HTTPCreated
206 end
207
208 def test_post
209 VCR.use_cassette('post_breaches') do
210 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
211 response = subject.post(uri)
212 refute_nil response
213 assert_equal 'Congratulations!', JSON.parse(response.body)['Message']
214 end
215 end
216
217 def test_post_with_block_syntax
218 VCR.use_cassette('post_breaches') do
219 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
220 subject.post(uri) do |_request, response|
221 @response = response
222 end
223 refute_nil @response
224 assert_equal 'Congratulations!', JSON.parse(@response.body)['Message']
225 end
226 end
227
228 def test_put
229 VCR.use_cassette('put_breaches') do
230 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
231 body = { command: 'echo hello' }
232 response = subject.put(uri, body: body)
233 refute_nil response
234 assert_equal 'Congratulations!', JSON.parse(response.body)['Message']
235 end
236 end
237
238 def test_put_with_block_syntax
239 VCR.use_cassette('put_breaches') do
240 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
241 body = { command: 'echo hello' }
242 subject.put(uri, body: body) do |_request, response|
243 @response = response
244 end
245 refute_nil @response
246 assert_equal 'Congratulations!', JSON.parse(@response.body)['Message']
247 end
248 end
249
250 def test_delete
251 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
252 VCR.use_cassette('delete_breaches') do
253 response = subject.delete(uri)
254 refute_nil response
255 assert_equal 'Congratulations!', JSON.parse(response.body)['Message']
256 end
257 end
258
259 def test_client_tls
260 private_key = OpenSSL::PKey::RSA.new(2048)
261 certificate = OpenSSL::X509::Certificate.new
262 certificate.not_after = certificate.not_before = Time.now
263 certificate.public_key = private_key.public_key
264 certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
265
266 subject = Net::Hippie::Client.new(certificate: certificate.to_pem, key: private_key.export)
267 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
268
269 @called = false
270 VCR.use_cassette('get_breaches') do
271 subject.get(uri) do |_request, response|
272 @called = true
273 refute_nil response
274 assert_equal '000webhost', JSON.parse(response.body)[0]['Title']
275 end
276 end
277 assert(@called)
278 end
279
280 def test_client_tls_with_passphrase
281 private_key = OpenSSL::PKey::RSA.new(2048)
282 passphrase = SecureRandom.hex(16)
283 certificate = OpenSSL::X509::Certificate.new
284 certificate.not_after = certificate.not_before = Time.now
285 certificate.public_key = private_key.public_key
286 certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
287
288 subject = Net::Hippie::Client.new(
289 certificate: certificate.to_pem,
290 key: private_key.export(OpenSSL::Cipher.new('AES-256-CBC'), passphrase),
291 passphrase: passphrase
292 )
293 uri = URI.parse('https://haveibeenpwned.com/api/breaches')
294
295 @called = false
296 VCR.use_cassette('get_breaches') do
297 subject.get(uri) do |_request, response|
298 @called = true
299 refute_nil response
300 assert_equal '000webhost', JSON.parse(response.body)[0]['Title']
301 end
302 end
303 assert(@called)
304 end
305
306 def test_logger
307 VCR.turned_off do
308 WebMock.allow_net_connect!
309 StringIO.open do |io|
310 subject = Net::Hippie::Client.new(logger: Logger.new(io, level: :debug))
311 response = subject.get('https://www.example.org/')
312
313 refute_nil response
314 assert_kind_of Net::HTTPOK, response
315 io.rewind
316 assert_match %r{^(opening connection to www.example.org:443)}, io.read
317 end
318 end
319 end
320end