komoju 0.0.0 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,398 @@
1
+ require 'helper'
2
+
3
+ class LinkTest < MiniTest::Unit::TestCase
4
+ include ExconHelper
5
+
6
+ # Link.run invokes a request against the service identified by the URL. The
7
+ # path is left unchanged when parameters aren't required and the username
8
+ # and password from the URL are passed using HTTP basic auth.
9
+ def test_run_without_parameters_and_with_empty_response
10
+ Excon.stub(method: :get) do |request|
11
+ assert_equal('Basic dXNlcm5hbWU6c2VjcmV0',
12
+ request[:headers]['Authorization'])
13
+ assert_equal('example.com', request[:host])
14
+ assert_equal(443, request[:port])
15
+ assert_equal('/resource', request[:path])
16
+ Excon.stubs.pop
17
+ {status: 200, body: ''}
18
+ end
19
+
20
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
21
+ link = Heroics::Link.new('https://username:secret@example.com',
22
+ schema.resource('resource').link('list'))
23
+ assert_equal(nil, link.run)
24
+ end
25
+
26
+ # Link.run injects parameters into the path in the order they were received.
27
+ def test_run_with_parameters_and_empty_response
28
+ Excon.stub(method: :get) do |request|
29
+ assert_equal('/resource/44724831-bf66-4bc2-865f-e2c4c2b14c78',
30
+ request[:path])
31
+ Excon.stubs.pop
32
+ {status: 200, body: ''}
33
+ end
34
+
35
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
36
+ link = Heroics::Link.new('https://example.com',
37
+ schema.resource('resource').link('info'))
38
+ assert_equal(nil, link.run('44724831-bf66-4bc2-865f-e2c4c2b14c78'))
39
+ end
40
+
41
+ # Link.run URL-escapes special characters in parameters.
42
+ def test_run_with_parameters_needing_escaping
43
+ Excon.stub(method: :get) do |request|
44
+ assert_equal('/resource/foo%23bar', request[:path])
45
+ Excon.stubs.pop
46
+ {status: 200, body: ''}
47
+ end
48
+
49
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
50
+ link = Heroics::Link.new('https://example.com',
51
+ schema.resource('resource').link('info'))
52
+ assert_equal(nil, link.run('foo#bar'))
53
+ end
54
+
55
+ # Link.run converts Time parameters to UTC before sending them to the
56
+ # server.
57
+ def test_run_converts_time_parameters_to_utc
58
+ Excon.stub(method: :delete) do |request|
59
+ assert_equal("/resource/2013-01-01T08:00:00Z", request[:path])
60
+ Excon.stubs.pop
61
+ {status: 200, body: ''}
62
+ end
63
+
64
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
65
+ link = Heroics::Link.new('https://example.com',
66
+ schema.resource('resource').link('delete'))
67
+ assert_equal(nil, link.run(Time.parse('2013-01-01 00:00:00-0800')))
68
+ end
69
+
70
+ # Link.run optionally takes an extra parameter to send in the request body.
71
+ # It automatically converts the specified object to JSON and includes a
72
+ # Content-Type header in the request.
73
+ def test_run_without_parameters_and_with_request_body
74
+ body = {'Hello' => 'world!'}
75
+ Excon.stub(method: :post) do |request|
76
+ assert_equal('application/json', request[:headers]['Content-Type'])
77
+ assert_equal(body, MultiJson.load(request[:body]))
78
+ Excon.stubs.pop
79
+ {status: 200, body: ''}
80
+ end
81
+
82
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
83
+ link = Heroics::Link.new('https://example.com',
84
+ schema.resource('resource').link('create'))
85
+ assert_equal(nil, link.run(body))
86
+ end
87
+
88
+ # Link.run optionally takes an extra parameter to send in the request body.
89
+ # It automatically converts the specified object to the specified encoding
90
+ # type and includes a Content-Type header in the request
91
+ def test_run_without_parameters_and_with_non_json_request_body
92
+ body = {'Hello' => 'world!'}
93
+ Excon.stub(method: :post) do |request|
94
+ assert_equal('application/x-www-form-urlencoded', request[:headers]['Content-Type'])
95
+ assert_equal('Hello=world%21', request[:body])
96
+ Excon.stubs.pop
97
+ {status: 200, body: ''}
98
+ end
99
+
100
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
101
+ link = Heroics::Link.new('https://example.com',
102
+ schema.resource('resource').link('submit'))
103
+ assert_equal(nil, link.run(body))
104
+ end
105
+
106
+
107
+ # Link.run passes custom headers to the server when they've been provided.
108
+ def test_run_with_custom_request_headers
109
+ Excon.stub(method: :get) do |request|
110
+ assert_equal('application/vnd.heroku+json; version=3',
111
+ request[:headers]['Accept'])
112
+ Excon.stubs.pop
113
+ {status: 200}
114
+ end
115
+
116
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
117
+ link = Heroics::Link.new(
118
+ 'https://example.com', schema.resource('resource').link('list'),
119
+ {default_headers: {'Accept' => 'application/vnd.heroku+json; version=3'}})
120
+ assert_equal(nil, link.run())
121
+ end
122
+
123
+ # Link.run passes custom headers to the server when they've been provided.
124
+ # It merges in the Content-Type when a body is included in the request.
125
+ def test_run_with_custom_request_headers_and_with_request_body
126
+ body = {'Hello' => 'world!'}
127
+ Excon.stub(method: :post) do |request|
128
+ assert_equal('application/json', request[:headers]['Content-Type'])
129
+ assert_equal('application/vnd.heroku+json; version=3',
130
+ request[:headers]['Accept'])
131
+ assert_equal(body, MultiJson.load(request[:body]))
132
+ Excon.stubs.pop
133
+ {status: 200}
134
+ end
135
+
136
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
137
+ link = Heroics::Link.new(
138
+ 'https://example.com', schema.resource('resource').link('create'),
139
+ default_headers: {'Accept' => 'application/vnd.heroku+json; version=3'})
140
+ assert_equal(nil, link.run(body))
141
+ end
142
+
143
+ # Link.run doesn't mutate the default headers.
144
+ def test_run_never_overwrites_default_headers
145
+ body = {'Hello' => 'world!'}
146
+ Excon.stub(method: :post) do |request|
147
+ assert_equal('application/json', request[:headers]['Content-Type'])
148
+ assert_equal('application/vnd.heroku+json; version=3',
149
+ request[:headers]['Accept'])
150
+ assert_equal(body, MultiJson.load(request[:body]))
151
+ Excon.stubs.pop
152
+ {status: 200}
153
+ end
154
+
155
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
156
+ link = Heroics::Link.new(
157
+ 'https://example.com', schema.resource('resource').link('create'),
158
+ {default_headers: {'Accept' => 'application/vnd.heroku+json; version=3'}})
159
+ assert_equal(nil, link.run(body))
160
+
161
+ # The second time we use the link, without providing a request body, the
162
+ # Content-Type set during the first run is not present, as expected.
163
+ Excon.stub(method: :post) do |request|
164
+ assert_equal(nil, request[:headers]['Content-Type'])
165
+ assert_equal('application/vnd.heroku+json; version=3',
166
+ request[:headers]['Accept'])
167
+ Excon.stubs.pop
168
+ {status: 200}
169
+ end
170
+ assert_equal(nil, link.run)
171
+ end
172
+
173
+ # Link.run returns text responses sent by the server without processing them
174
+ # in any way.
175
+ def test_run_with_text_response
176
+ Excon.stub(method: :get) do |request|
177
+ assert_equal('/resource', request[:path])
178
+ Excon.stubs.pop
179
+ {status: 200, headers: {'Content-Type' => 'application/text'},
180
+ body: "Hello, world!\r\n"}
181
+ end
182
+
183
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
184
+ link = Heroics::Link.new('https://example.com',
185
+ schema.resource('resource').link('list'))
186
+ assert_equal("Hello, world!\r\n", link.run)
187
+ end
188
+
189
+ # Link.run automatically decodes JSON responses sent by the server into Ruby
190
+ # objects.
191
+ def test_run_with_json_response
192
+ body = {'Hello' => 'World!'}
193
+ Excon.stub(method: :post) do |request|
194
+ assert_equal('/resource', request[:path])
195
+ Excon.stubs.pop
196
+ {status: 201, headers: {'Content-Type' => 'application/json'},
197
+ body: MultiJson.dump(body)}
198
+ end
199
+
200
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
201
+ link = Heroics::Link.new('https://example.com',
202
+ schema.resource('resource').link('create'))
203
+ assert_equal(body, link.run)
204
+ end
205
+
206
+ # Link.run automatically decodes JSON responses with a complex Content-Type
207
+ # header sent by the server into Ruby objects.
208
+ def test_run_with_json_response_and_complex_content_type
209
+ body = {'Hello' => 'World!'}
210
+ Excon.stub(method: :get) do |request|
211
+ assert_equal('/resource', request[:path])
212
+ Excon.stubs.pop
213
+ {status: 200,
214
+ headers: {'Content-Type' => 'application/vnd.api+json;charset=utf-8'},
215
+ body: MultiJson.dump(body)}
216
+ end
217
+
218
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
219
+ link = Heroics::Link.new('https://example.com',
220
+ schema.resource('resource').link('list'))
221
+ assert_equal(body, link.run)
222
+ end
223
+
224
+ # Link.run considers HTTP 202 Accepted responses as successful.
225
+ def test_run_with_accepted_request
226
+ body = {'Hello' => 'World!'}
227
+ Excon.stub(method: :post) do |request|
228
+ assert_equal('/resource', request[:path])
229
+ Excon.stubs.pop
230
+ {status: 202, headers: {'Content-Type' => 'application/json'},
231
+ body: MultiJson.dump(body)}
232
+ end
233
+
234
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
235
+ link = Heroics::Link.new('https://example.com',
236
+ schema.resource('resource').link('create'))
237
+ assert_equal(body, link.run)
238
+ end
239
+
240
+ # Link.run considers HTTP 204 No Content responses as successful.
241
+ def test_run_with_no_content_response
242
+ Excon.stub(method: :delete) do |request|
243
+ assert_equal("/resource/2013-01-01T08:00:00Z", request[:path])
244
+ Excon.stubs.pop
245
+ {status: 204, body: ''}
246
+ end
247
+
248
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
249
+ link = Heroics::Link.new('https://example.com',
250
+ schema.resource('resource').link('delete'))
251
+ assert_equal(nil, link.run(Time.parse('2013-01-01 00:00:00-0800')))
252
+ end
253
+
254
+ # Link.run raises an Excon error if anything other than a 200 or 201 HTTP
255
+ # status code was returned by the server.
256
+ def test_run_with_failed_request
257
+ Excon.stub(method: :get) do |request|
258
+ assert_equal('/resource', request[:path])
259
+ Excon.stubs.pop
260
+ {status: 400}
261
+ end
262
+
263
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
264
+ link = Heroics::Link.new('https://example.com',
265
+ schema.resource('resource').link('list'))
266
+ assert_raises Excon::Errors::BadRequest do
267
+ link.run
268
+ end
269
+ end
270
+
271
+ # Link.run raises an ArgumentError if too few parameters are provided.
272
+ def test_run_with_missing_parameters
273
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
274
+ link = Heroics::Link.new('https://example.com',
275
+ schema.resource('resource').link('info'))
276
+ error = assert_raises ArgumentError do
277
+ link.run
278
+ end
279
+ assert_equal('wrong number of arguments (0 for 1)', error.message)
280
+ end
281
+
282
+ # Link.run raises an ArgumentError if too many parameters are provided.
283
+ def test_run_with_too_many_parameters
284
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
285
+ link = Heroics::Link.new('https://example.com',
286
+ schema.resource('resource').link('info'))
287
+ error = assert_raises ArgumentError do
288
+ link.run('too', 'many', 'parameters')
289
+ end
290
+ assert_equal('wrong number of arguments (3 for 1)', error.message)
291
+ end
292
+
293
+ # Link.run passes ETags from the cache to the server with GET requests.
294
+ def test_run_passes_cached_etags_in_get_requests
295
+ Excon.stub(method: :get) do |request|
296
+ assert_equal('etag-contents', request[:headers]['If-None-Match'])
297
+ Excon.stubs.pop
298
+ {status: 200}
299
+ end
300
+
301
+ headers = {}
302
+ cache = Moneta.new(:Memory)
303
+ cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
304
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
305
+ link = Heroics::Link.new('https://example.com',
306
+ schema.resource('resource').link('list'),
307
+ cache: cache)
308
+ link.run
309
+ end
310
+
311
+ # Link.run will not pas ETags from the cache for non-GET requests.
312
+ def test_run_ignores_etags_for_non_get_requests
313
+ Excon.stub(method: :post) do |request|
314
+ assert_equal(nil, request[:headers]['If-None-Match'])
315
+ Excon.stubs.pop
316
+ {status: 201}
317
+ end
318
+
319
+ cache = Moneta.new(:Memory)
320
+ cache['etag:/resource:0'] = 'etag-contents'
321
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
322
+ link = Heroics::Link.new('https://example.com',
323
+ schema.resource('resource').link('create'),
324
+ cache: cache)
325
+ link.run({'Hello' => 'World'})
326
+ end
327
+
328
+ # Link.run returns JSON content loaded from the cache when a GET request
329
+ # with an ETag yields a 304 Not Modified response.
330
+ def test_run_returns_cached_json_content_for_not_modified_response
331
+ body = {'Hello' => 'World!'}
332
+ Excon.stub(method: :get) do |request|
333
+ assert_equal('etag-contents', request[:headers]['If-None-Match'])
334
+ Excon.stubs.pop
335
+ {status: 304, headers: {'Content-Type' => 'application/json'}}
336
+ end
337
+
338
+ headers = {}
339
+ cache = Moneta.new(:Memory)
340
+ cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
341
+ cache["data:/resource:#{headers.hash}"] = MultiJson.dump(body)
342
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
343
+ link = Heroics::Link.new('https://example.com',
344
+ schema.resource('resource').link('list'),
345
+ cache: cache)
346
+ assert_equal(body, link.run)
347
+ end
348
+
349
+ # Link.run caches JSON content received from the server when an ETag is
350
+ # included in the response.
351
+ def test_run_caches_json_body_when_an_etag_is_received
352
+ body = {'Hello' => 'World!'}
353
+ Excon.stub(method: :get) do |request|
354
+ Excon.stubs.pop
355
+ {status: 200, headers: {'Content-Type' => 'application/json',
356
+ 'ETag' => 'etag-contents'},
357
+ body: MultiJson.dump(body)}
358
+ end
359
+
360
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
361
+ link = Heroics::Link.new('https://example.com',
362
+ schema.resource('resource').link('list'),
363
+ cache: Moneta.new(:Memory))
364
+ assert_equal(body, link.run)
365
+
366
+ Excon.stub(method: :get) do |request|
367
+ assert_equal('etag-contents', request[:headers]['If-None-Match'])
368
+ Excon.stubs.pop
369
+ {status: 304, headers: {'Content-Type' => 'application/json'}}
370
+ end
371
+ assert_equal(body, link.run)
372
+ end
373
+
374
+ # Link.run returns an enumerator when a 206 Partial Content status code and
375
+ # Content-Range header is included in a server response. The enumerator
376
+ # makes requests to fetch missing pages as its iterated.
377
+ def test_run_with_range_response
378
+ Excon.stub(method: :get) do |request|
379
+ Excon.stubs.shift
380
+ {status: 206, headers: {'Content-Type' => 'application/json',
381
+ 'Content-Range' => 'id 1..2; max=200'},
382
+ body: MultiJson.dump([2])}
383
+ end
384
+
385
+ Excon.stub(method: :get) do |request|
386
+ Excon.stubs.shift
387
+ {status: 206, headers: {'Content-Type' => 'application/json',
388
+ 'Content-Range' => 'id 0..1; max=200',
389
+ 'Next-Range' => '201'},
390
+ body: MultiJson.dump([1])}
391
+ end
392
+
393
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
394
+ link = Heroics::Link.new('https://example.com',
395
+ schema.resource('resource').link('list'))
396
+ assert_equal([1, 2], link.run.to_a)
397
+ end
398
+ end
@@ -0,0 +1,45 @@
1
+ require 'helper'
2
+
3
+ class RubyNameTest < MiniTest::Unit::TestCase
4
+ # ruby_name is a no-op when an empty string is provided.
5
+ def test_ruby_name_with_empty_name
6
+ assert_equal('', Heroics.ruby_name(''))
7
+ end
8
+
9
+ # ruby_name converts capitals in a name to lowercase.
10
+ def test_ruby_name_with_capitals
11
+ assert_equal('capitalizedname', Heroics.ruby_name('CapitalizedName'))
12
+ end
13
+
14
+ # ruby_name converts dashes in a name to underscores.
15
+ def test_ruby_name_with_dashes
16
+ assert_equal('dashed_name', Heroics.ruby_name('dashed-name'))
17
+ end
18
+
19
+ # ruby_name converts spaces in a name to underscores.
20
+ def test_ruby_name_with_spaces
21
+ assert_equal('spaced_name', Heroics.ruby_name('spaced name'))
22
+ end
23
+ end
24
+
25
+ class PrettyNameTest < MiniTest::Unit::TestCase
26
+ # pretty_name is a no-op when an empty string is provided.
27
+ def test_pretty_name_with_empty_name
28
+ assert_equal('', Heroics.pretty_name(''))
29
+ end
30
+
31
+ # pretty_name converts capitals in a name to lowercase.
32
+ def test_pretty_name_with_capitals
33
+ assert_equal('capitalizedname', Heroics.pretty_name('CapitalizedName'))
34
+ end
35
+
36
+ # pretty_name converts underscores in a name to dashes.
37
+ def test_pretty_name_with_underscores
38
+ assert_equal('dashed-name', Heroics.pretty_name('dashed_name'))
39
+ end
40
+
41
+ # pretty_name converts spaces in a name to underscores.
42
+ def test_pretty_name_with_spaces
43
+ assert_equal('spaced-name', Heroics.pretty_name('spaced name'))
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ require 'helper'
2
+
3
+ class ResourceTest < MiniTest::Unit::TestCase
4
+ include ExconHelper
5
+
6
+ # Resource.<link> raises a NoMethodError when a method is invoked without a
7
+ # matching link.
8
+ def test_invalid_link
9
+ resource = Heroics::Resource.new({})
10
+ error = assert_raises NoMethodError do
11
+ resource.unknown
12
+ end
13
+ assert_match(
14
+ /undefined method `unknown' for #<Heroics::Resource:0x.*>/,
15
+ error.message)
16
+ end
17
+
18
+ # Resource.<link> finds the appropriate link and invokes it.
19
+ def test_link
20
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
21
+ link = Heroics::Link.new('https://username:secret@example.com',
22
+ schema.resource('resource').link('list'))
23
+ resource = Heroics::Resource.new({'link' => link})
24
+ Excon.stub(method: :get) do |request|
25
+ assert_equal('Basic dXNlcm5hbWU6c2VjcmV0',
26
+ request[:headers]['Authorization'])
27
+ assert_equal('example.com', request[:host])
28
+ assert_equal(443, request[:port])
29
+ assert_equal('/resource', request[:path])
30
+ Excon.stubs.pop
31
+ {status: 200, body: 'Hello, world!'}
32
+ end
33
+ assert_equal('Hello, world!', resource.link)
34
+ end
35
+ end