api-transformer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,746 @@
1
+ require "goliath/test_helper"
2
+ require "goliath/test_helper_streaming"
3
+
4
+ require_relative "./spec_helper"
5
+ require_relative "../lib/api_transformer"
6
+
7
+ def create_server(&block)
8
+ server = Class.new(ApiTransformer::Server) do
9
+ base_url "http://localhost:9900"
10
+ end
11
+
12
+ server.instance_eval(&block)
13
+
14
+ server
15
+ end
16
+
17
+ def extend_server(server, &block)
18
+ server.instance_eval(&block)
19
+ server
20
+ end
21
+
22
+ def echo_greeting_server
23
+ create_server do
24
+ get "/echo_greeting" do |params|
25
+ response do
26
+ attribute :greeting, params[:greeting]
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe ApiTransformer::Server do
33
+ include Goliath::TestHelper
34
+
35
+ let(:err) { proc { |c| fail "HTTP Request failed #{c.response}" } }
36
+
37
+ before do
38
+ ApiTransformer::Server.reset_routes
39
+ end
40
+
41
+ it "works without any backend requests" do
42
+ no_backend = create_server do
43
+ get "/ping" do
44
+ response do
45
+ success { attribute :result, "pong" }
46
+ end
47
+ end
48
+ end
49
+
50
+ with_api(no_backend) do
51
+ get_request(path: "/ping") do |c|
52
+ c.response.must_equal '{"result":"pong"}'
53
+ end
54
+ end
55
+ end
56
+
57
+ it "works with a backend request" do
58
+ backend = create_server do
59
+ get "/one" do
60
+ request :two do
61
+ path "/two"
62
+ method :get
63
+ end
64
+
65
+ response do |data|
66
+ success { attribute :two, data[:two].json }
67
+ end
68
+ end
69
+
70
+ get "/two" do
71
+ response do
72
+ success { attribute :result, "OK" }
73
+ end
74
+ end
75
+ end
76
+
77
+ with_api(backend) do
78
+ get_request(path: "/one") do |c|
79
+ c.response.must_equal '{"two":{"result":"OK"}}'
80
+ end
81
+ end
82
+ end
83
+
84
+ it "fails without a method on backend requests" do
85
+ server = create_server do
86
+ get "/nomethod" do
87
+ request :somewhere do
88
+ path "/somewhere"
89
+ end
90
+
91
+ response do
92
+ success 204
93
+ end
94
+ end
95
+ end
96
+
97
+ with_api(server) do
98
+ get_request(path: "/nomethod") do |c|
99
+ c.response.must_equal "Missing method for backend request: somewhere"
100
+ end
101
+ end
102
+ end
103
+
104
+ it "works with a namespace" do
105
+ namspaced = create_server do
106
+ namespace "/first" do
107
+ get "/second" do
108
+ response do
109
+ success { attribute :result, "Found me!" }
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ with_api(namspaced) do
116
+ get_request(path: "/first/second") do |c|
117
+ c.response.must_equal '{"result":"Found me!"}'
118
+ end
119
+ end
120
+ end
121
+
122
+ it "accepts path params from incoming requests" do
123
+ server = create_server do
124
+ get "/echo_greeting/:greeting" do |params|
125
+ response do
126
+ attribute :greeting, params[:greeting]
127
+ end
128
+ end
129
+ end
130
+
131
+ with_api(server) do
132
+ get_request(path: "/echo_greeting/hola") do |c|
133
+ c.response.must_equal '{"greeting":"hola"}'
134
+ end
135
+ end
136
+ end
137
+
138
+ it "accepts query params from incoming requests" do
139
+ with_api(echo_greeting_server) do
140
+ get_request(path: "/echo_greeting", query: { greeting: "yo" }) do |c|
141
+ c.response.must_equal '{"greeting":"yo"}'
142
+ end
143
+ end
144
+ end
145
+
146
+ it "accepts form params from incoming requests" do
147
+ with_api(echo_greeting_server) do
148
+ get_request(path: "/echo_greeting", body: { greeting: "hullo" }) do |c|
149
+ c.response.must_equal '{"greeting":"hullo"}'
150
+ end
151
+ end
152
+ end
153
+
154
+ it "accepts json params from incoming requests" do
155
+ with_api(echo_greeting_server) do
156
+ get_request(
157
+ path: "/echo_greeting",
158
+ body: '{"greeting":"howdy"}',
159
+ head: { "Content-Type" => "application/json" }
160
+ ) do |c|
161
+ c.response.must_equal '{"greeting":"howdy"}'
162
+ end
163
+ end
164
+ end
165
+
166
+ it "sends query params in backend requests" do
167
+ proxy_query = extend_server echo_greeting_server do
168
+ get "/proxy_query_param" do
169
+ request :echo do
170
+ path "/echo_greeting"
171
+ method :get
172
+
173
+ query_param :greeting, "hello"
174
+ end
175
+
176
+ response do |data|
177
+ attribute :greeting, data[:echo][:greeting]
178
+ end
179
+ end
180
+ end
181
+
182
+ with_api(proxy_query) do
183
+ get_request(path: "/proxy_query_param") do |c|
184
+ c.response.must_equal '{"greeting":"hello"}'
185
+ end
186
+ end
187
+ end
188
+
189
+ it "sends form params in backend requests" do
190
+ proxy_form = extend_server echo_greeting_server do
191
+ get "/proxy_form_param" do
192
+ request :echo do
193
+ path "/echo_greeting"
194
+ method :get
195
+
196
+ form_param :greeting, "hello"
197
+ end
198
+
199
+ response do |data|
200
+ attribute :greeting, data[:echo][:greeting]
201
+ end
202
+ end
203
+ end
204
+
205
+ with_api(proxy_form) do
206
+ get_request(path: "/proxy_form_param") do |c|
207
+ c.response.must_equal '{"greeting":"hello"}'
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "json params" do
213
+ it "sends json params in backend requests" do
214
+ proxy_json = extend_server echo_greeting_server do
215
+ get "/proxy_json_param" do
216
+ request :echo do
217
+ path "/echo_greeting"
218
+ method :get
219
+
220
+ json_param :greeting, "hello"
221
+ end
222
+
223
+ response do |data|
224
+ attribute :greeting, data[:echo][:greeting]
225
+ end
226
+ end
227
+ end
228
+
229
+ with_api(proxy_json) do
230
+ get_request(path: "/proxy_json_param") do |c|
231
+ c.response.must_equal '{"greeting":"hello"}'
232
+ end
233
+ end
234
+ end
235
+
236
+ it "does not allow both json and form params together" do
237
+ conflicting_params = create_server do
238
+ get "/conflicting_params" do
239
+ request :echo do
240
+ path "/echo_greeting"
241
+ method :get
242
+
243
+ json_param :greeting, "hello"
244
+ form_param :name, "Bob"
245
+ end
246
+
247
+ response do |data|
248
+ attribute :greeting, data[:echo][:greeting]
249
+ end
250
+ end
251
+ end
252
+
253
+ with_api(conflicting_params) do
254
+ get_request(path: "/conflicting_params") do |c|
255
+ c.response.must_equal "A request cannot have both json and form " \
256
+ "parameters"
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ # TODO: make this test work
263
+ # It works if the /proxy_cookie_param and /echo_greeting endpoints are run
264
+ # in an actual server instance, but not here in the test.
265
+ #
266
+ # it "sends cookie params in backend requests" do
267
+ # proxy_cookie = extend_server echo_greeting_server do
268
+ # get "/proxy_cookie_param" do
269
+ # request :echo do
270
+ # path "/echo_greeting"
271
+ # method :get
272
+
273
+ # cookie_param :greeting, "hello"
274
+ # end
275
+
276
+ # response do |data|
277
+ # attribute :greeting, data[:echo][:greeting]
278
+ # end
279
+ # end
280
+ # end
281
+
282
+ # with_api(proxy_cookie) do
283
+ # get_request(path: "/proxy_cookie_param") do |c|
284
+ # c.response.must_equal '{"greeting":"hello"}'
285
+ # end
286
+ # end
287
+ # end
288
+
289
+ it "handles frontend post requests" do
290
+ frontend_post = create_server do
291
+ post "/ping" do
292
+ response do
293
+ success { attribute :result, "pong" }
294
+ end
295
+ end
296
+ end
297
+
298
+ with_api(frontend_post) do
299
+ post_request(path: "/ping") do |c|
300
+ c.response.must_equal '{"result":"pong"}'
301
+ end
302
+ end
303
+ end
304
+
305
+ it "handles frontend put requests" do
306
+ frontend_post = create_server do
307
+ put "/ping" do
308
+ response do
309
+ success { attribute :result, "pong" }
310
+ end
311
+ end
312
+ end
313
+
314
+ with_api(frontend_post) do
315
+ put_request(path: "/ping") do |c|
316
+ c.response.must_equal '{"result":"pong"}'
317
+ end
318
+ end
319
+ end
320
+
321
+ it "handles frontend delete requests" do
322
+ frontend_post = create_server do
323
+ delete "/ping" do
324
+ response do
325
+ success { attribute :result, "pong" }
326
+ end
327
+ end
328
+ end
329
+
330
+ with_api(frontend_post) do
331
+ delete_request(path: "/ping") do |c|
332
+ c.response.must_equal '{"result":"pong"}'
333
+ end
334
+ end
335
+ end
336
+
337
+ it "passes frontend request headers through to backend requests" do
338
+ echo_header = create_server do
339
+ get "/proxy" do
340
+ request :echo do
341
+ path "/echo"
342
+ method :get
343
+ end
344
+
345
+ response do |data|
346
+ success { attribute :result, data[:echo][:header] }
347
+ end
348
+ end
349
+
350
+ get "/echo" do |_, headers|
351
+ response do
352
+ success { attribute :header, headers["Bob-Content"] }
353
+ end
354
+ end
355
+ end
356
+
357
+ with_api(echo_header) do
358
+ get_request(
359
+ path: "/proxy",
360
+ head: { "Bob-Content" => "bob/classic" }
361
+ ) do |c|
362
+ c.response.must_equal '{"result":"bob/classic"}'
363
+ end
364
+ end
365
+ end
366
+
367
+ it "can set the entire body of the reponse" do
368
+ server = create_server do
369
+ get "/endpoint" do
370
+ response do
371
+ success { body "free form text!" }
372
+ end
373
+ end
374
+ end
375
+
376
+ with_api(server) do
377
+ get_request(path: "/endpoint") do |c|
378
+ c.response.must_equal "free form text!"
379
+ end
380
+ end
381
+ end
382
+
383
+ it "can respond with objects" do
384
+ class Bob
385
+ def initialize(last_name)
386
+ @last_name = last_name
387
+ end
388
+
389
+ def to_hash
390
+ { first_name: "Bob", last_name: @last_name }
391
+ end
392
+ end
393
+
394
+ object_response = create_server do
395
+ get "/object" do
396
+ response do
397
+ success { object :bob, Bob, "Saget" }
398
+ end
399
+ end
400
+ end
401
+
402
+ with_api(object_response) do
403
+ get_request(path: "/object") do |c|
404
+ c.response.must_equal '{"bob":{"first_name":"Bob","last_name":"Saget"}}'
405
+ end
406
+ end
407
+ end
408
+
409
+ it "can respond with arrays" do
410
+ # Example serialization class
411
+ class Bob
412
+ def initialize(last_name)
413
+ @last_name = last_name
414
+ end
415
+
416
+ def to_hash
417
+ { first_name: "Bob", last_name: @last_name }
418
+ end
419
+ end
420
+
421
+ object_response = create_server do
422
+ get "/object" do
423
+ response do
424
+ success { array :bobs, Bob, %w(Ross Marley) }
425
+ end
426
+ end
427
+ end
428
+
429
+ with_api(object_response) do
430
+ get_request(path: "/object") do |c|
431
+ c.response.must_equal '{"bobs":[' \
432
+ '{"first_name":"Bob","last_name":"Ross"},' \
433
+ '{"first_name":"Bob","last_name":"Marley"}' \
434
+ "]}"
435
+ end
436
+ end
437
+ end
438
+
439
+ it "can respond with cookies" do
440
+ server = create_server do
441
+ get "/cookie" do
442
+ response do
443
+ success { cookie "cookie-monster", "mmm... cookie" }
444
+ end
445
+ end
446
+ end
447
+
448
+ with_api(server) do
449
+ get_request(path: "/cookie") do |c|
450
+ expected = "cookie-monster=mmm... cookie"
451
+ c.response_header["Set-Cookie"].must_equal expected
452
+ end
453
+ end
454
+ end
455
+
456
+ it "can respond with a specified content type" do
457
+ server = create_server do
458
+ get "/endpoint" do
459
+ response do
460
+ success { content_type "image/jpeg" }
461
+ end
462
+ end
463
+ end
464
+
465
+ with_api(server) do
466
+ get_request(path: "/endpoint") do |c|
467
+ c.response_header["CONTENT_TYPE"].must_equal "image/jpeg"
468
+ end
469
+ end
470
+ end
471
+
472
+ it "makes the results of requests available to subsequent requests" do
473
+ multiple_requests = create_server do
474
+ get "/multi" do
475
+ request :one do
476
+ path "/one"
477
+ method :get
478
+ end
479
+
480
+ request :two do |data|
481
+ path "/two"
482
+ method :get
483
+
484
+ query_param :name, data[:one][:name]
485
+ end
486
+
487
+ response do |data|
488
+ success do
489
+ attribute :name, data[:two][:result]
490
+ cookie "type", data[:two].cookies[:type]
491
+ end
492
+ end
493
+ end
494
+
495
+ get "/one" do
496
+ response do
497
+ success { attribute :name, "Bob" }
498
+ end
499
+ end
500
+
501
+ get "/two" do |params|
502
+ response do
503
+ success do
504
+ attribute :result, params[:name]
505
+ cookie "type", "delicious"
506
+ end
507
+ end
508
+ end
509
+ end
510
+
511
+ with_api(multiple_requests) do
512
+ get_request(path: "/multi") do |c|
513
+ c.response.must_equal '{"name":"Bob"}'
514
+ c.response_header["Set-Cookie"].must_equal "type=delicious"
515
+ end
516
+ end
517
+ end
518
+
519
+ it "allows conditional requests" do
520
+ server = create_server do
521
+ get "/conditional" do
522
+ request :nonexistent, when: false do
523
+ path "/nonexistent"
524
+ method :get
525
+ end
526
+
527
+ response do
528
+ success { attribute :result, "OK" }
529
+ end
530
+ end
531
+ end
532
+
533
+ with_api(server) do
534
+ get_request(path: "/conditional") do |c|
535
+ c.response.must_equal '{"result":"OK"}'
536
+ end
537
+ end
538
+ end
539
+
540
+ it "streams downloads" do
541
+ server = create_server do
542
+ get "/one" do
543
+ stream true
544
+
545
+ request :two do
546
+ path "/two"
547
+ method :get
548
+ end
549
+
550
+ response do |data|
551
+ success do
552
+ stream do
553
+ data[:two].stream do |chunk|
554
+ stream_write chunk
555
+ end
556
+ end
557
+ end
558
+ end
559
+ end
560
+
561
+ get "/two" do
562
+ stream true
563
+
564
+ response do
565
+ success do
566
+ stream do
567
+ (1..9).each { |n| stream_write n }
568
+ end
569
+ end
570
+ end
571
+ end
572
+ end
573
+
574
+ with_api(server) do
575
+ get_request(path: "/one") do |c|
576
+ c.response.must_equal "123456789"
577
+ end
578
+ end
579
+ end
580
+
581
+ it "supports failure handlers in the response declaration" do
582
+ server = create_server do
583
+ get "/fail" do
584
+ response do
585
+ failure true do
586
+ status 422
587
+ attribute :result, "fail"
588
+ end
589
+ end
590
+ end
591
+ end
592
+
593
+ with_api(server) do
594
+ get_request(path: "/fail") do |c|
595
+ c.response.must_equal '{"result":"fail"}'
596
+ end
597
+ end
598
+ end
599
+
600
+ it "responds with the first matching failure handler" do
601
+ server = create_server do
602
+ get "/doublefail" do
603
+ response do
604
+ failure true do
605
+ attribute :fail, "one"
606
+ end
607
+
608
+ failure true do
609
+ attribute :fail, "two"
610
+ end
611
+ end
612
+ end
613
+ end
614
+
615
+ with_api(server) do
616
+ get_request(path: "/doublefail") do |c|
617
+ c.response.must_equal '{"fail":"one"}'
618
+ end
619
+ end
620
+ end
621
+
622
+ it "checks unhandled failures" do
623
+ server = create_server do
624
+ unhandled_failures do |data|
625
+ failure data.status == 400 do
626
+ status 422
627
+ attribute :fail, "yep"
628
+ end
629
+ end
630
+
631
+ get "/unhandled" do
632
+ request :fail do
633
+ path "/fail"
634
+ method :get
635
+ end
636
+
637
+ response do
638
+ success { attribute :result, "win" }
639
+ end
640
+ end
641
+
642
+ get "/fail" do
643
+ response do
644
+ success do
645
+ status 400
646
+ end
647
+ end
648
+ end
649
+ end
650
+
651
+ with_api(server) do
652
+ get_request(path: "/unhandled") do |c|
653
+ c.response.must_equal '{"fail":"yep"}'
654
+ end
655
+ end
656
+ end
657
+
658
+ it "passes through completely unhandled failures" do
659
+ server = create_server do
660
+ get "/completely_unhandled" do
661
+ request :fail do
662
+ path "/fail"
663
+ method :get
664
+ end
665
+
666
+ response do
667
+ success { attribute :result, "win" }
668
+ end
669
+ end
670
+
671
+ get "/fail" do
672
+ response do
673
+ success do
674
+ status 400
675
+ attribute :oh, "no"
676
+ end
677
+ end
678
+ end
679
+ end
680
+
681
+ with_api(server) do
682
+ get_request(path: "/completely_unhandled") do |c|
683
+ c.response.must_equal '{"oh":"no"}'
684
+ end
685
+ end
686
+ end
687
+
688
+ it "does not crash when there are no routes for an HTTP verb" do
689
+ server = create_server do
690
+ get "/ping" do
691
+ response do
692
+ success { attribute :result, "pong" }
693
+ end
694
+ end
695
+ end
696
+
697
+ with_api(server) do
698
+ post_request(path: "/whatever") do |c|
699
+ c.response.must_equal "nope"
700
+ end
701
+ end
702
+ end
703
+
704
+ it "allows a shorthand for setting success response status" do
705
+ server = create_server do
706
+ get "/no_body" do
707
+ response do
708
+ success 204
709
+ end
710
+ end
711
+ end
712
+
713
+ with_api(server) do
714
+ get_request(path: "/no_body") do |c|
715
+ c.response_header.http_status.must_equal 204
716
+ c.response.must_equal ""
717
+ end
718
+ end
719
+ end
720
+
721
+ it "provides a way to add helper methods" do
722
+ server = create_server do
723
+ helpers do
724
+ def victory_attribute
725
+ attribute :result, "VICTORY"
726
+ end
727
+
728
+ def victory!
729
+ response do
730
+ success { victory_attribute }
731
+ end
732
+ end
733
+ end
734
+
735
+ get "/status" do
736
+ victory!
737
+ end
738
+ end
739
+
740
+ with_api(server) do
741
+ get_request(path: "/status") do |c|
742
+ c.response.must_equal '{"result":"VICTORY"}'
743
+ end
744
+ end
745
+ end
746
+ end