komoju 0.0.0 → 0.0.3

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.
@@ -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