agoo 2.5.7 → 2.6.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.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +38 -0
  4. data/ext/agoo/agoo.c +11 -2
  5. data/ext/agoo/bind.c +15 -20
  6. data/ext/agoo/con.c +32 -25
  7. data/ext/agoo/debug.c +225 -162
  8. data/ext/agoo/debug.h +31 -51
  9. data/ext/agoo/doc.c +278 -5
  10. data/ext/agoo/doc.h +6 -1
  11. data/ext/agoo/err.c +1 -0
  12. data/ext/agoo/err.h +1 -0
  13. data/ext/agoo/error_stream.c +3 -6
  14. data/ext/agoo/gqlcobj.c +12 -0
  15. data/ext/agoo/gqlcobj.h +25 -0
  16. data/ext/agoo/gqleval.c +520 -0
  17. data/ext/agoo/gqleval.h +49 -0
  18. data/ext/agoo/gqlintro.c +1237 -97
  19. data/ext/agoo/gqlintro.h +8 -0
  20. data/ext/agoo/gqljson.c +460 -0
  21. data/ext/agoo/gqljson.h +15 -0
  22. data/ext/agoo/gqlvalue.c +679 -136
  23. data/ext/agoo/gqlvalue.h +29 -7
  24. data/ext/agoo/graphql.c +841 -362
  25. data/ext/agoo/graphql.h +180 -90
  26. data/ext/agoo/hook.c +8 -16
  27. data/ext/agoo/http.c +3 -4
  28. data/ext/agoo/log.c +22 -25
  29. data/ext/agoo/log.h +1 -0
  30. data/ext/agoo/page.c +24 -40
  31. data/ext/agoo/pub.c +23 -21
  32. data/ext/agoo/queue.c +2 -4
  33. data/ext/agoo/ready.c +9 -9
  34. data/ext/agoo/req.c +80 -5
  35. data/ext/agoo/req.h +2 -0
  36. data/ext/agoo/res.c +1 -3
  37. data/ext/agoo/rgraphql.c +753 -0
  38. data/ext/agoo/rresponse.c +9 -15
  39. data/ext/agoo/rserver.c +18 -17
  40. data/ext/agoo/sdl.c +1264 -120
  41. data/ext/agoo/sdl.h +8 -1
  42. data/ext/agoo/sectime.c +136 -0
  43. data/ext/agoo/sectime.h +19 -0
  44. data/ext/agoo/server.c +1 -3
  45. data/ext/agoo/subject.c +2 -4
  46. data/ext/agoo/text.c +124 -18
  47. data/ext/agoo/text.h +5 -1
  48. data/ext/agoo/upgraded.c +2 -4
  49. data/lib/agoo/version.rb +1 -1
  50. data/test/base_handler_test.rb +43 -40
  51. data/test/bind_test.rb +49 -48
  52. data/test/graphql_test.rb +1019 -0
  53. data/test/hijack_test.rb +1 -1
  54. data/test/rack_handler_test.rb +40 -34
  55. data/test/static_test.rb +33 -32
  56. metadata +17 -6
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.5.7'
4
+ VERSION = '2.6.0'
5
5
  end
@@ -15,6 +15,7 @@ require 'oj'
15
15
  require 'agoo'
16
16
 
17
17
  class BaseHandlerTest < Minitest::Test
18
+ @@server_started = false
18
19
 
19
20
  class TellMeHandler
20
21
  def initialize
@@ -42,45 +43,47 @@ class BaseHandlerTest < Minitest::Test
42
43
  end
43
44
  end
44
45
 
45
- def test_base_handler
46
- begin
47
- Agoo::Log.configure(dir: '',
48
- console: true,
49
- classic: true,
50
- colorize: true,
51
- states: {
52
- INFO: false,
53
- DEBUG: false,
54
- connect: false,
55
- request: false,
56
- response: false,
57
- eval: true,
58
- })
59
-
60
- Agoo::Server.init(6470, 'root', thread_count: 1)
61
-
62
- handler = TellMeHandler.new
63
-
64
- Agoo::Server.handle(:GET, "/tellme", handler)
65
- Agoo::Server.handle(:POST, "/makeme", handler)
66
- Agoo::Server.handle(:PUT, "/makeme", handler)
67
- Agoo::Server.handle(:GET, "/wild/*/one", WildHandler.new('one'))
68
- Agoo::Server.handle(:GET, "/wild/all/**", WildHandler.new('all'))
69
-
70
- Agoo::Server.start()
71
-
72
- #sleep(100)
73
- eval_test
74
- post_test
75
- put_test
76
- wild_one_test
77
- wild_all_test
78
- ensure
79
- Agoo.shutdown
46
+ def start_server
47
+ Agoo::Log.configure(dir: '',
48
+ console: true,
49
+ classic: true,
50
+ colorize: true,
51
+ states: {
52
+ INFO: false,
53
+ DEBUG: false,
54
+ connect: false,
55
+ request: false,
56
+ response: false,
57
+ eval: true,
58
+ })
59
+
60
+ Agoo::Server.init(6470, 'root', thread_count: 1)
61
+
62
+ handler = TellMeHandler.new
63
+
64
+ Agoo::Server.handle(:GET, "/tellme", handler)
65
+ Agoo::Server.handle(:POST, "/makeme", handler)
66
+ Agoo::Server.handle(:PUT, "/makeme", handler)
67
+ Agoo::Server.handle(:GET, "/wild/*/one", WildHandler.new('one'))
68
+ Agoo::Server.handle(:GET, "/wild/all/**", WildHandler.new('all'))
69
+
70
+ Agoo::Server.start()
71
+
72
+ @@server_started = true
73
+ end
74
+
75
+ def setup
76
+ unless @@server_started
77
+ start_server
80
78
  end
81
79
  end
80
+
81
+ Minitest.after_run {
82
+ GC.start
83
+ Agoo::shutdown
84
+ }
82
85
 
83
- def eval_test
86
+ def test_eval
84
87
  uri = URI('http://localhost:6470/tellme?a=1')
85
88
  req = Net::HTTP::Get.new(uri)
86
89
  # Set the headers the way we want them.
@@ -122,7 +125,7 @@ class BaseHandlerTest < Minitest::Test
122
125
  }
123
126
  end
124
127
 
125
- def post_test
128
+ def test_post
126
129
  uri = URI('http://localhost:6470/makeme')
127
130
  req = Net::HTTP::Post.new(uri)
128
131
  # Set the headers the way we want them.
@@ -136,7 +139,7 @@ class BaseHandlerTest < Minitest::Test
136
139
  assert_equal(Net::HTTPNoContent, res.class)
137
140
  end
138
141
 
139
- def put_test
142
+ def test_put
140
143
  uri = URI('http://localhost:6470/makeme')
141
144
  req = Net::HTTP::Put.new(uri)
142
145
  # Set the headers the way we want them.
@@ -152,7 +155,7 @@ class BaseHandlerTest < Minitest::Test
152
155
  assert_equal('hello', res.body)
153
156
  end
154
157
 
155
- def wild_one_test
158
+ def test_wild_one
156
159
  uri = URI('http://localhost:6470/wild/abc/one')
157
160
  req = Net::HTTP::Get.new(uri)
158
161
  res = Net::HTTP.start(uri.hostname, uri.port) { |h|
@@ -161,7 +164,7 @@ class BaseHandlerTest < Minitest::Test
161
164
  assert_equal('one - /wild/abc/one', res.body)
162
165
  end
163
166
 
164
- def wild_all_test
167
+ def test_wild_all
165
168
  uri = URI('http://localhost:6470/wild/all/x/y')
166
169
  req = Net::HTTP::Get.new(uri)
167
170
  res = Net::HTTP.start(uri.hostname, uri.port) { |h|
@@ -14,81 +14,82 @@ require 'socket'
14
14
  require 'agoo'
15
15
 
16
16
  class BindTest < Minitest::Test
17
+ @@server_started = false
18
+ @@addr = '127.0.0.1'
19
+ @@addr6 = '::1'
20
+ @@name = '/tmp/agoo_test.socket'
17
21
 
18
- # Run all the tests in one test to avoid creating the server multiple times.
19
- def test_static
20
- begin
21
- Agoo::Log.configure(dir: '',
22
- console: true,
23
- classic: true,
24
- colorize: true,
25
- states: {
26
- INFO: false,
27
- DEBUG: false,
28
- connect: false,
29
- request: false,
30
- response: false,
31
- eval: true,
32
- })
22
+ def start_server
23
+ Agoo::Log.configure(dir: '',
24
+ console: true,
25
+ classic: true,
26
+ colorize: true,
27
+ states: {
28
+ INFO: false,
29
+ DEBUG: false,
30
+ connect: false,
31
+ request: false,
32
+ response: false,
33
+ eval: true,
34
+ })
33
35
 
34
- addr = '127.0.0.1'
35
- Socket.ip_address_list.each { |ai|
36
- if ai.ipv4? && '127.0.0.1' != ai.ip_address
37
- addr = ai.ip_address
38
- break
39
- end
40
- }
41
- addr6 = '::1'
42
- name = '/tmp/agoo_test.socket'
43
- Agoo::Server.init(6471, 'root', thread_count: 1,
44
- bind: ['http://127.0.0.1:6472',
45
- "http://#{addr}:6473",
46
- "http://[#{addr6}]:6474",
47
- "unix://#{name}",
48
- ])
49
- Agoo::Server.start()
36
+ Socket.ip_address_list.each { |ai|
37
+ if ai.ipv4? && '127.0.0.1' != ai.ip_address
38
+ @@addr = ai.ip_address
39
+ break
40
+ end
41
+ }
42
+ Agoo::Server.init(6471, 'root', thread_count: 1,
43
+ bind: ['http://127.0.0.1:6472',
44
+ "http://#{@@addr}:6473",
45
+ "http://[#{@@addr6}]:6474",
46
+ "unix://#{@@name}",
47
+ ])
48
+ Agoo::Server.start()
49
+ @@server_started = true
50
+ end
50
51
 
51
- port_test
52
- localhost_test
53
- ipv4_test(addr)
54
- ipv6_test(addr6)
55
- restrict_test unless '127.0.0.1' == addr
56
- named_test(name)
57
- ensure
58
- Agoo::shutdown
52
+ def setup
53
+ unless @@server_started
54
+ start_server
59
55
  end
60
56
  end
61
57
 
62
- def port_test
58
+ Minitest.after_run {
59
+ Agoo::shutdown
60
+ }
61
+
62
+ def test_port
63
63
  uri = URI('http://localhost:6471/index.html')
64
64
  request(uri)
65
65
  end
66
66
 
67
- def localhost_test
67
+ def test_localhost
68
68
  uri = URI('http://localhost:6472/index.html')
69
69
  request(uri)
70
70
  end
71
71
 
72
- def ipv4_test(addr)
73
- uri = URI("http://#{addr}:6473/index.html")
72
+ def test_ipv4_test
73
+ uri = URI("http://#{@@addr}:6473/index.html")
74
74
  request(uri)
75
75
  end
76
76
 
77
- def ipv6_test(addr)
78
- uri = URI("http://[#{addr}]:6474/index.html")
77
+ def test_ipv6_test
78
+ uri = URI("http://[#{@@addr6}]:6474/index.html")
79
79
  request(uri)
80
80
  end
81
81
 
82
- def restrict_test
82
+ def test_restrict
83
+ return if '127.0.0.1' == @@addr
83
84
  uri = URI("http://127.0.0.1:6473/index.html")
84
85
  assert_raises Exception do
85
86
  Net::HTTP.get(uri)
86
87
  end
87
88
  end
88
89
 
89
- def named_test(name)
90
+ def test_named_test
90
91
  content = ''
91
- UNIXSocket.open(name) {|c|
92
+ UNIXSocket.open(@@name) {|c|
92
93
  c.print(%|GET /index.html HTTP/1.1\r
93
94
  Accept-Encoding: *\r
94
95
  Accept: */*\r
@@ -0,0 +1,1019 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $root_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
5
+ %w(lib ext).each do |dir|
6
+ $: << File.join($root_dir, dir)
7
+ end
8
+
9
+ require 'minitest'
10
+ require 'minitest/autorun'
11
+ require 'net/http'
12
+
13
+ require 'oj'
14
+ require 'agoo'
15
+
16
+ class Artist
17
+ attr_reader :name
18
+ attr_reader :songs
19
+ attr_reader :origin
20
+ attr_reader :likes
21
+
22
+ def initialize(name, origin)
23
+ @name = name
24
+ @songs = []
25
+ @origin = origin
26
+ @likes = 0
27
+ end
28
+
29
+ def song(args={})
30
+ @songs[args['name']]
31
+ end
32
+
33
+ # Only used by the Song to add itself to the artist.
34
+ def add_song(song)
35
+ @songs << song
36
+ end
37
+
38
+ def genre_songs(args={})
39
+ g = args['genre']
40
+ a = []
41
+ @songs.each { |s|
42
+ a << s if g == s.genre
43
+ }
44
+ a
45
+ end
46
+
47
+ def songs_after(args={})
48
+ t = args['time']
49
+ a = []
50
+ @songs.each { |s|
51
+ a << s if t <= s.release
52
+ }
53
+ a
54
+ end
55
+
56
+ def like
57
+ @likes += 1
58
+ end
59
+
60
+ end
61
+
62
+ class Song
63
+ attr_reader :name # string
64
+ attr_reader :artist # reference
65
+ attr_reader :duration # integer
66
+ attr_reader :release # time
67
+ attr_reader :genre # string
68
+
69
+ def initialize(name, artist, duration, release)
70
+ @name = name
71
+ @artist = artist
72
+ @duration = duration
73
+ @release = release
74
+ @genre = 'INDIE'
75
+ artist.add_song(self)
76
+ end
77
+ end
78
+
79
+ $songs_sdl = %^
80
+ type Query @ruby(class: "Query") {
81
+ artist(name: String!): Artist
82
+ }
83
+
84
+ type Mutation {
85
+ like(artist: String!): Artist
86
+ }
87
+
88
+ type Artist @ruby(class: "Artist") {
89
+ name: String!
90
+ songs: [Song]
91
+ origin: [String]
92
+ genre_songs(genre:Genre): [Song]
93
+ songs_after(time: Time): [Song]
94
+ likes: Int
95
+ }
96
+
97
+ type Song @ruby(class: "Song") {
98
+ name: String!
99
+ artist: Artist
100
+ duration: Int
101
+ genre: Genre
102
+ release: Time
103
+ }
104
+
105
+ enum Genre {
106
+ POP
107
+ ROCK
108
+ INDIE
109
+ }
110
+ ^
111
+
112
+ class Query
113
+ attr_reader :artists
114
+
115
+ def initialize(artists)
116
+ @artists = artists
117
+ end
118
+
119
+ def artist(args={})
120
+ @artists[args['name']]
121
+ end
122
+ end
123
+
124
+ class Mutation
125
+ attr_reader :artists
126
+
127
+ def initialize(artists)
128
+ @artists = artists
129
+ end
130
+
131
+ def like(args={})
132
+ artist = @artists[args['artist']]
133
+ artist.like
134
+ artist
135
+ end
136
+ end
137
+
138
+ class Schema
139
+ attr_reader :query
140
+ attr_reader :mutation
141
+ attr_reader :subscription
142
+
143
+ def initialize()
144
+ # Set up some data for testing.
145
+ artist = Artist.new('Fazerdaze', ['Morningside', 'Auckland', 'New Zealand'])
146
+ Song.new('Jennifer', artist, 240, Time.utc(2017, 5, 5))
147
+ Song.new('Lucky Girl', artist, 170, Time.utc(2017, 5, 5))
148
+ Song.new('Friends', artist, 194, Time.utc(2017, 5, 5))
149
+ Song.new('Reel', artist, 193, Time.utc(2015, 11, 2))
150
+ @artists = {artist.name => artist}
151
+
152
+ @query = Query.new(@artists)
153
+ @mutation = Mutation.new(@artists)
154
+ end
155
+ end
156
+
157
+ class GraphQLTest < Minitest::Test
158
+ @@server_started = false
159
+
160
+ SCHEMA_EXPECT = %^type schema @ruby(class: "Schema") {
161
+ query: Query
162
+ mutation: Mutation
163
+ subscription: Subscription
164
+ }
165
+
166
+ type Artist @ruby(class: "Artist") {
167
+ name: String!
168
+ songs: [Song]
169
+ origin: [String]
170
+ genre_songs(genre: Genre): [Song]
171
+ songs_after(time: Time): [Song]
172
+ likes: Int
173
+ }
174
+
175
+ type Mutation {
176
+ like(artist: String!): Artist
177
+ }
178
+
179
+ type Query @ruby(class: "Query") {
180
+ artist(name: String!): Artist
181
+ }
182
+
183
+ type Song @ruby(class: "Song") {
184
+ name: String!
185
+ artist: Artist
186
+ duration: Int
187
+ genre: Genre
188
+ release: Time
189
+ }
190
+
191
+ type Subscription {
192
+ }
193
+
194
+ enum Genre {
195
+ POP
196
+ ROCK
197
+ INDIE
198
+ }
199
+
200
+ directive @ruby(class: String!) on SCHEMA | OBJECT
201
+ ^
202
+
203
+ def start_server
204
+ Agoo::Log.configure(dir: '',
205
+ console: true,
206
+ classic: true,
207
+ colorize: true,
208
+ states: {
209
+ INFO: false,
210
+ DEBUG: false,
211
+ connect: false,
212
+ request: false,
213
+ response: false,
214
+ eval: true,
215
+ })
216
+ Agoo::Server.init(6472, 'root', thread_count: 1, graphql: '/graphql')
217
+ Agoo::Server.start()
218
+
219
+ Agoo::GraphQL.schema(Schema.new) {
220
+ Agoo::GraphQL.load($songs_sdl)
221
+ }
222
+
223
+ @@server_started = true
224
+ end
225
+
226
+ def setup
227
+ if @@server_started == false
228
+ start_server
229
+ end
230
+ end
231
+
232
+ Minitest.after_run {
233
+ GC.start
234
+ Agoo::shutdown
235
+ }
236
+
237
+ # TBD list arg
238
+ # TBD obj arg
239
+ # TBD introspection
240
+
241
+ ##################################
242
+
243
+ def test_load
244
+ content = Agoo::GraphQL.sdl_dump(with_descriptions: false, all: false)
245
+ content.force_encoding('UTF-8')
246
+ assert_equal(SCHEMA_EXPECT, content)
247
+ end
248
+
249
+ def test_get_schema
250
+ uri = URI('http://localhost:6472/graphql/schema?with_desc=false&all=false')
251
+ req = Net::HTTP::Get.new(uri)
252
+ req['Accept-Encoding'] = '*'
253
+ req['User-Agent'] = 'Ruby'
254
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
255
+ h.request(req)
256
+ }
257
+ content = res.body
258
+ assert_equal('application/graphql', res['Content-Type'])
259
+ assert_equal(SCHEMA_EXPECT, content)
260
+ end
261
+
262
+ def test_get_query
263
+ uri = URI('http://localhost:6472/graphql?query={artist(name:"Fazerdaze"){name}}&indent=2')
264
+ expect = %^{
265
+ "data":{
266
+ "artist":{
267
+ "name":"Fazerdaze"
268
+ }
269
+ }
270
+ }^
271
+ req = Net::HTTP::Get.new(uri)
272
+ req['Accept-Encoding'] = '*'
273
+ req['User-Agent'] = 'Ruby'
274
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
275
+ h.request(req)
276
+ }
277
+ content = res.body
278
+ assert_equal('application/json', res['Content-Type'])
279
+ assert_equal(expect, content)
280
+ end
281
+
282
+ def test_variable_query
283
+ uri = URI('http://localhost:6472/graphql?query={artist(name:"Fazerdaze"){name}}&indent=2')
284
+ expect = %^{
285
+ "data":{
286
+ "artist":{
287
+ "name":"Fazerdaze"
288
+ }
289
+ }
290
+ }^
291
+ req_test(uri, expect)
292
+ end
293
+
294
+ def test_alias_query
295
+ uri = URI('http://localhost:6472/graphql?query=query withVar($name:String="Fazerdaze"){artist(name:$name){name}}&indent=2')
296
+ expect = %^{
297
+ "data":{
298
+ "artist":{
299
+ "name":"Fazerdaze"
300
+ }
301
+ }
302
+ }^
303
+ req_test(uri, expect)
304
+ end
305
+
306
+ def test_parse_error
307
+ uri = URI('http://localhost:6472/graphql?query=nonsense')
308
+ expect = %^{
309
+ "errors":[
310
+ {
311
+ "message":"unexpected character at 1:1",
312
+ "code":"parse error"
313
+ }
314
+ ]
315
+ }
316
+ ^
317
+ req_test(uri, expect, 'errors.0.timestamp')
318
+ end
319
+
320
+ def test_json_vars_query
321
+ #uri = URI('http://localhost:6472/graphql?query={artist(name:$name){name}}&indent=2&variables={"name":"Fazerdaze"}')
322
+ uri = URI('http://localhost:6472/graphql?query={artist(name:"Fazerdaze"){name}}&indent=2&variables={"name":"Fazerdaze"}')
323
+ expect = %^{
324
+ "data":{
325
+ "artist":{
326
+ "name":"Fazerdaze"
327
+ }
328
+ }
329
+ }^
330
+ req_test(uri, expect)
331
+ end
332
+
333
+ def test_list_query
334
+ uri = URI('http://localhost:6472/graphql?query=query withVar($name:String="Fazerdaze"){artist(name:$name){name,songs{name}}}&indent=2')
335
+ expect = %^{
336
+ "data":{
337
+ "artist":{
338
+ "name":"Fazerdaze",
339
+ "songs":[
340
+ {
341
+ "name":"Jennifer"
342
+ },
343
+ {
344
+ "name":"Lucky Girl"
345
+ },
346
+ {
347
+ "name":"Friends"
348
+ },
349
+ {
350
+ "name":"Reel"
351
+ }
352
+ ]
353
+ }
354
+ }
355
+ }^
356
+ req_test(uri, expect)
357
+ end
358
+
359
+ def test_array_query
360
+ uri = URI('http://localhost:6472/graphql?query={artist(name:"Fazerdaze"){name,origin}}&indent=2')
361
+ expect = %^{
362
+ "data":{
363
+ "artist":{
364
+ "name":"Fazerdaze",
365
+ "origin":[
366
+ "Morningside",
367
+ "Auckland",
368
+ "New Zealand"
369
+ ]
370
+ }
371
+ }
372
+ }^
373
+ req_test(uri, expect)
374
+ end
375
+
376
+ def test_post_graphql
377
+ uri = URI('http://localhost:6472/graphql?indent=2')
378
+ body = %^{
379
+ artist(name:"Fazerdaze"){
380
+ name
381
+ }
382
+ }
383
+ ^
384
+ expect = %^{
385
+ "data":{
386
+ "artist":{
387
+ "name":"Fazerdaze"
388
+ }
389
+ }
390
+ }^
391
+
392
+ post_test(uri, body, 'application/graphql', expect)
393
+ end
394
+
395
+ def test_post_json
396
+ uri = URI('http://localhost:6472/graphql?indent=2')
397
+ body = %^{
398
+ "query":"{\\n artist(name:\\\"Fazerdaze\\\"){\\n name\\n }\\n}"
399
+ }^
400
+ expect = %^{
401
+ "data":{
402
+ "artist":{
403
+ "name":"Fazerdaze"
404
+ }
405
+ }
406
+ }^
407
+
408
+ post_test(uri, body, 'application/json', expect)
409
+ end
410
+
411
+ def test_post_fragment
412
+ uri = URI('http://localhost:6472/graphql?indent=2')
413
+ body = %^
414
+ {
415
+ artist(name:"Fazerdaze") {
416
+ ...basic
417
+ }
418
+ }
419
+
420
+ fragment basic on Artist {
421
+ name
422
+ origin
423
+ }
424
+ ^
425
+ expect = %^{
426
+ "data":{
427
+ "artist":{
428
+ "name":"Fazerdaze",
429
+ "origin":[
430
+ "Morningside",
431
+ "Auckland",
432
+ "New Zealand"
433
+ ]
434
+ }
435
+ }
436
+ }^
437
+
438
+ post_test(uri, body, 'application/graphql', expect)
439
+ end
440
+
441
+ def test_post_inline
442
+ uri = URI('http://localhost:6472/graphql?indent=2')
443
+ body = %^
444
+ {
445
+ artist(name:"Fazerdaze") {
446
+ ... on Artist {
447
+ name
448
+ origin
449
+ }
450
+ }
451
+ }
452
+ ^
453
+ expect = %^{
454
+ "data":{
455
+ "artist":{
456
+ "name":"Fazerdaze",
457
+ "origin":[
458
+ "Morningside",
459
+ "Auckland",
460
+ "New Zealand"
461
+ ]
462
+ }
463
+ }
464
+ }^
465
+
466
+ post_test(uri, body, 'application/graphql', expect)
467
+ end
468
+
469
+ def test_post_skip
470
+ uri = URI('http://localhost:6472/graphql?indent=2')
471
+ body = %^
472
+ query skippy($boo: Boolean = true){
473
+ artist(name:"Fazerdaze") {
474
+ ... @include(if: true) {
475
+ name
476
+ }
477
+ ... @skip(if: true) {
478
+ origin
479
+ }
480
+ }
481
+ }
482
+ ^
483
+ expect = %^{
484
+ "data":{
485
+ "artist":{
486
+ "name":"Fazerdaze"
487
+ }
488
+ }
489
+ }^
490
+
491
+ post_test(uri, body, 'application/graphql', expect)
492
+ end
493
+
494
+ def test_post_nested
495
+ uri = URI('http://localhost:6472/graphql?indent=2')
496
+ body = %^
497
+ query skippy($boo: Boolean = true){
498
+ artist(name:"Fazerdaze") {
499
+ name
500
+ __typename
501
+ songs {
502
+ name
503
+ __typename
504
+ duration
505
+ }
506
+ }
507
+ }
508
+ ^
509
+ expect = %^{
510
+ "data":{
511
+ "artist":{
512
+ "name":"Fazerdaze",
513
+ "__typename":"Artist",
514
+ "songs":[
515
+ {
516
+ "name":"Jennifer",
517
+ "__typename":"Song",
518
+ "duration":240
519
+ },
520
+ {
521
+ "name":"Lucky Girl",
522
+ "__typename":"Song",
523
+ "duration":170
524
+ },
525
+ {
526
+ "name":"Friends",
527
+ "__typename":"Song",
528
+ "duration":194
529
+ },
530
+ {
531
+ "name":"Reel",
532
+ "__typename":"Song",
533
+ "duration":193
534
+ }
535
+ ]
536
+ }
537
+ }
538
+ }^
539
+
540
+ post_test(uri, body, 'application/graphql', expect)
541
+ end
542
+
543
+ def test_post_variables
544
+ uri = URI('http://localhost:6472/graphql?indent=2')
545
+ body = %^
546
+ query skippy($boo: Boolean = true){
547
+ artist(name:"Fazerdaze") {
548
+ ... @include(if: $boo) {
549
+ name
550
+ }
551
+ ... @skip(if: $boo) {
552
+ origin
553
+ }
554
+ }
555
+ }
556
+ ^
557
+ expect = %^{
558
+ "data":{
559
+ "artist":{
560
+ "name":"Fazerdaze"
561
+ }
562
+ }
563
+ }^
564
+
565
+ post_test(uri, body, 'application/graphql', expect)
566
+ end
567
+
568
+ def test_post_enum
569
+ uri = URI('http://localhost:6472/graphql?indent=2')
570
+ body = %^
571
+ {
572
+ artist(name:"Fazerdaze") {
573
+ name
574
+ songs: genre_songs(genre: INDIE) {
575
+ name
576
+ genre
577
+ }
578
+ }
579
+ }
580
+ ^
581
+ expect = %^{
582
+ "data":{
583
+ "artist":{
584
+ "name":"Fazerdaze",
585
+ "songs":[
586
+ {
587
+ "name":"Jennifer",
588
+ "genre":"INDIE"
589
+ },
590
+ {
591
+ "name":"Lucky Girl",
592
+ "genre":"INDIE"
593
+ },
594
+ {
595
+ "name":"Friends",
596
+ "genre":"INDIE"
597
+ },
598
+ {
599
+ "name":"Reel",
600
+ "genre":"INDIE"
601
+ }
602
+ ]
603
+ }
604
+ }
605
+ }^
606
+
607
+ post_test(uri, body, 'application/graphql', expect)
608
+ end
609
+
610
+ def test_post_time
611
+ uri = URI('http://localhost:6472/graphql?indent=2')
612
+ body = %^
613
+ {
614
+ artist(name:"Fazerdaze") {
615
+ songs: songs_after(time: "2016-01-01T00:00:00.000000000Z") {
616
+ name
617
+ release
618
+ }
619
+ }
620
+ }
621
+ ^
622
+ expect = %^{
623
+ "data":{
624
+ "artist":{
625
+ "songs":[
626
+ {
627
+ "name":"Jennifer",
628
+ "release":"2017-05-05T00:00:00.000000000Z"
629
+ },
630
+ {
631
+ "name":"Lucky Girl",
632
+ "release":"2017-05-05T00:00:00.000000000Z"
633
+ },
634
+ {
635
+ "name":"Friends",
636
+ "release":"2017-05-05T00:00:00.000000000Z"
637
+ }
638
+ ]
639
+ }
640
+ }
641
+ }^
642
+
643
+ post_test(uri, body, 'application/graphql', expect)
644
+ end
645
+
646
+ def test_intro_type
647
+ uri = URI('http://localhost:6472/graphql?query={__type(name:"Artist"){kind,name,description}}&indent=2')
648
+ expect = %^{
649
+ "data":{
650
+ "__type":{
651
+ "kind":"OBJECT",
652
+ "name":"Artist",
653
+ "description":null
654
+ }
655
+ }
656
+ }^
657
+ req_test(uri, expect)
658
+ end
659
+
660
+ def test_intro_of_type
661
+ uri = URI('http://localhost:6472/graphql?query={__type(name:"[__Type!]"){name,ofType{name}}}&indent=2')
662
+ expect = %^{
663
+ "data":{
664
+ "__type":{
665
+ "name":"[__Type!]",
666
+ "ofType":{
667
+ "name":"__Type"
668
+ }
669
+ }
670
+ }
671
+ }^
672
+ req_test(uri, expect)
673
+ end
674
+
675
+ def test_intro_enum_values
676
+ uri = URI('http://localhost:6472/graphql?query={__type(name:"__TypeKind"){name,choices:enumValues{name,isDeprecated}}}&indent=2')
677
+ expect = %^{
678
+ "data":{
679
+ "__type":{
680
+ "name":"__TypeKind",
681
+ "choices":[
682
+ {
683
+ "name":"SCALAR",
684
+ "isDeprecated":false
685
+ },
686
+ {
687
+ "name":"OBJECT",
688
+ "isDeprecated":false
689
+ },
690
+ {
691
+ "name":"INTERFACE",
692
+ "isDeprecated":false
693
+ },
694
+ {
695
+ "name":"UNION",
696
+ "isDeprecated":false
697
+ },
698
+ {
699
+ "name":"ENUM",
700
+ "isDeprecated":false
701
+ },
702
+ {
703
+ "name":"INPUT_OBJECT",
704
+ "isDeprecated":false
705
+ },
706
+ {
707
+ "name":"LIST",
708
+ "isDeprecated":false
709
+ },
710
+ {
711
+ "name":"NON_NULL",
712
+ "isDeprecated":false
713
+ }
714
+ ]
715
+ }
716
+ }
717
+ }^
718
+ req_test(uri, expect)
719
+ end
720
+
721
+ def test_intro_fields
722
+ uri = URI('http://localhost:6472/graphql?query={__type(name:"Artist"){name,fields{name,type{name},args{name,type{name},defaultValue}}}}&indent=2')
723
+ expect = %^{
724
+ "data":{
725
+ "__type":{
726
+ "name":"Artist",
727
+ "fields":[
728
+ {
729
+ "name":"name",
730
+ "type":{
731
+ "name":"String"
732
+ },
733
+ "args":[
734
+ ]
735
+ },
736
+ {
737
+ "name":"songs",
738
+ "type":{
739
+ "name":"[Song]"
740
+ },
741
+ "args":[
742
+ ]
743
+ },
744
+ {
745
+ "name":"origin",
746
+ "type":{
747
+ "name":"[String]"
748
+ },
749
+ "args":[
750
+ ]
751
+ },
752
+ {
753
+ "name":"genre_songs",
754
+ "type":{
755
+ "name":"[Song]"
756
+ },
757
+ "args":[
758
+ {
759
+ "name":"genre",
760
+ "type":{
761
+ "name":"Genre"
762
+ },
763
+ "defaultValue":null
764
+ }
765
+ ]
766
+ },
767
+ {
768
+ "name":"songs_after",
769
+ "type":{
770
+ "name":"[Song]"
771
+ },
772
+ "args":[
773
+ {
774
+ "name":"time",
775
+ "type":{
776
+ "name":"Time"
777
+ },
778
+ "defaultValue":null
779
+ }
780
+ ]
781
+ },
782
+ {
783
+ "name":"likes",
784
+ "type":{
785
+ "name":"Int"
786
+ },
787
+ "args":[
788
+ ]
789
+ }
790
+ ]
791
+ }
792
+ }
793
+ }^
794
+ req_test(uri, expect)
795
+ end
796
+
797
+ def test_intro_schema_types
798
+ uri = URI('http://localhost:6472/graphql?query={__schema{types{name}}}&indent=2')
799
+ expect = %^{
800
+ "data":{
801
+ "__schema":{
802
+ "types":[
803
+ {
804
+ "name":"__EnumValue"
805
+ },
806
+ {
807
+ "name":"Int"
808
+ },
809
+ {
810
+ "name":"Subscription"
811
+ },
812
+ {
813
+ "name":"I64"
814
+ },
815
+ {
816
+ "name":"__DirectiveLocation"
817
+ },
818
+ {
819
+ "name":"Time"
820
+ },
821
+ {
822
+ "name":"Genre"
823
+ },
824
+ {
825
+ "name":"__Schema"
826
+ },
827
+ {
828
+ "name":"Mutation"
829
+ },
830
+ {
831
+ "name":"Uuid"
832
+ },
833
+ {
834
+ "name":"Boolean"
835
+ },
836
+ {
837
+ "name":"schema"
838
+ },
839
+ {
840
+ "name":"String"
841
+ },
842
+ {
843
+ "name":"Song"
844
+ },
845
+ {
846
+ "name":"Artist"
847
+ },
848
+ {
849
+ "name":"__Directive"
850
+ },
851
+ {
852
+ "name":"__Field"
853
+ },
854
+ {
855
+ "name":"__TypeKind"
856
+ },
857
+ {
858
+ "name":"Query"
859
+ },
860
+ {
861
+ "name":"__InputValue"
862
+ },
863
+ {
864
+ "name":"__Type"
865
+ },
866
+ {
867
+ "name":"Float"
868
+ }
869
+ ]
870
+ }
871
+ }
872
+ }^
873
+ req_test(uri, expect)
874
+ end
875
+
876
+ def test_intro_schema_query_type
877
+ uri = URI('http://localhost:6472/graphql?query={__schema{queryType{name}}}&indent=2')
878
+ expect = %^{
879
+ "data":{
880
+ "__schema":{
881
+ "queryType":{
882
+ "name":"Query"
883
+ }
884
+ }
885
+ }
886
+ }^
887
+ req_test(uri, expect)
888
+ end
889
+
890
+ def test_intro_schema_directives
891
+ uri = URI('http://localhost:6472/graphql?query={__schema{directives{name,locations,args{name}}}}&indent=2')
892
+ expect = %^{
893
+ "data":{
894
+ "__schema":{
895
+ "directives":[
896
+ {
897
+ "name":"ruby",
898
+ "locations":[
899
+ "SCHEMA",
900
+ "OBJECT"
901
+ ],
902
+ "args":[
903
+ {
904
+ "name":"class"
905
+ }
906
+ ]
907
+ },
908
+ {
909
+ "name":"skip",
910
+ "locations":[
911
+ "FIELD",
912
+ "FRAGMENT_SPREAD",
913
+ "INLINE_FRAGMENT"
914
+ ],
915
+ "args":[
916
+ {
917
+ "name":"if"
918
+ }
919
+ ]
920
+ },
921
+ {
922
+ "name":"include",
923
+ "locations":[
924
+ "FIELD",
925
+ "FRAGMENT_SPREAD",
926
+ "INLINE_FRAGMENT"
927
+ ],
928
+ "args":[
929
+ {
930
+ "name":"if"
931
+ }
932
+ ]
933
+ },
934
+ {
935
+ "name":"deprecated",
936
+ "locations":[
937
+ "FIELD_DEFINITION",
938
+ "ENUM_VALUE"
939
+ ],
940
+ "args":[
941
+ {
942
+ "name":"reason"
943
+ }
944
+ ]
945
+ }
946
+ ]
947
+ }
948
+ }
949
+ }^
950
+ req_test(uri, expect)
951
+ end
952
+
953
+ def test_mutation
954
+ uri = URI('http://localhost:6472/graphql?indent=2')
955
+ body = %^
956
+ mutation {
957
+ like(artist:"Fazerdaze") {
958
+ name
959
+ likes
960
+ }
961
+ }
962
+ ^
963
+ expect = %^{
964
+ "data":{
965
+ "like":{
966
+ "name":"Fazerdaze",
967
+ "likes":1
968
+ }
969
+ }
970
+ }^
971
+
972
+ post_test(uri, body, 'application/graphql', expect)
973
+ end
974
+
975
+
976
+
977
+ ##################################
978
+
979
+ def deep_delete(obj, path)
980
+ key = path[0]
981
+ if 1 == path.length
982
+ obj.delete(key)
983
+ else
984
+ if key == key.to_i.to_s
985
+ deep_delete(obj[key.to_i], path[1..-1])
986
+ else
987
+ deep_delete(obj[key], path[1..-1])
988
+ end
989
+ end
990
+ end
991
+
992
+ def req_test(uri, expect, ignore=nil)
993
+ req = Net::HTTP::Get.new(uri)
994
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
995
+ h.request(req)
996
+ }
997
+ content = res.body
998
+ unless ignore.nil?
999
+ result = Oj.load(content, mode: :strict)
1000
+ deep_delete(result, ignore.split('.'))
1001
+ content = Oj.dump(result, indent: 2)
1002
+ end
1003
+ assert_equal(expect, content)
1004
+ end
1005
+
1006
+ def post_test(uri, body, content_type, expect)
1007
+ uri = URI(uri)
1008
+ req = Net::HTTP::Post.new(uri)
1009
+ req['Accept-Encoding'] = '*'
1010
+ req['Content-Type'] = content_type
1011
+ req.body = body
1012
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
1013
+ h.request(req)
1014
+ }
1015
+ content = res.body
1016
+ assert_equal('application/json', res['Content-Type'])
1017
+ assert_equal(expect, content)
1018
+ end
1019
+ end