1 module parsertests;
2 
3 import std.format : format;
4 import std.experimental.allocator;
5 import std.experimental.allocator.mallocator : Mallocator;
6 import std.stdio;
7 
8 import lexer;
9 import parser;
10 
11 private struct TestCase {
12 	int id;
13 	string str;
14 }
15 
16 unittest {
17 	TestCase[] tests;
18 	tests ~= TestCase(0, 
19 	`
20 mutation updateUser($userId: String! $name: String!) {  
21   updateUser(id: $userId name: $name) {
22     name
23   }
24 }
25 `);
26 
27 	tests ~= TestCase(1,`
28 query inlineFragmentNoType($expandedInfo: Boolean) {
29   user(handle: "zuck") {
30     id
31     name
32     ... @include(if: $expandedInfo) {
33       firstName
34       lastName
35       birthday
36     }
37   }
38 }
39 `);
40 
41 	tests ~= TestCase(2, `
42 query HeroForEpisode($ep: Episode!) {
43   hero(episode: $ep) {
44     name
45     ... on Droid {
46       primaryFunction
47     }
48     ... on Human {
49       height
50     }
51   }
52 }
53 `);
54 
55 	tests ~= TestCase(3, `
56 mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
57   createReview(episode: $ep, review: $review) {
58     stars
59     commentary
60   }
61 }
62 `);
63 
64 	tests ~= TestCase(4, `
65 query HeroNameAndFriends($episode: Episode = "JEDI") {
66   hero(episode: $episode) {
67     name
68     friends {
69       name
70     }
71   }
72 }`);
73 
74 	tests ~= TestCase(5, `
75 query  hero {
76     name
77     # Queries can have comments!
78 
79     # Queries can have comments Another!
80     friends {
81       name
82     }
83 }`);
84 
85 	tests ~= TestCase(6, `
86 enum DogCommand { SIT, DOWN, HEEL }
87 
88 type Dog implements Pet {
89   name: String!
90   nickname: String
91   barkVolume: Int
92   doesKnowCommand(dogCommand: DogCommand!): Boolean!
93   isHousetrained(atOtherHomes: Boolean): Boolean!
94   owner: Human
95 }
96 
97 interface Sentient {
98   name: String!
99 }
100 
101 interface Pet {
102   name: String!
103 }
104 
105 type Alien implements Sentient {
106   name: String!
107   homePlanet: String
108 }
109 
110 type Human implements Sentient {
111   name: String!
112 }
113 
114 enum CatCommand { JUMP }
115 
116 type Cat implements Pet {
117   name: String!
118   nickname: String
119   doesKnowCommand(catCommand: CatCommand!): Boolean!
120   meowVolume: Int
121 }
122 
123 union CatOrDog = Cat | Dog
124 union DogOrHuman = Dog | Human
125 union HumanOrAlien = Human | Alien
126 
127 type QueryRoot {
128   dog: Dog
129 }
130 `);
131 
132 	tests ~= TestCase(7, `
133 union SearchResult = Photo | Person
134 union SearchResult = Photo Person
135 
136 type Person {
137   name: String
138   age: Int
139 }
140 
141 type Photo {
142   height: Int
143   width: Int
144 }
145 
146 type SearchQuery {
147   firstSearchResult: SearchResult
148 }`);
149 
150 	tests ~= TestCase(8, `
151 interface NamedEntity {
152   name: String
153 }
154 
155 type Person implements NamedEntity {
156   name: String
157   age: Int
158 }
159 
160 type Business implements NamedEntity {
161   name: String
162   employeeCount: Int
163 }`);
164 
165 	tests ~= TestCase(9, `type Person {
166   name: String
167   age: Int
168   picture: Url
169 }`);
170 
171 	tests ~= TestCase(10, `
172 query myQuery($someTest: Boolean) {
173   experimentalField @skip(if: $someTest)
174 }`);
175 
176 	tests ~= TestCase(11, `
177 subscription sub {
178   newMessage {
179     body
180     sender
181   }
182 }
183 
184 fragment newMessageFields on Message {
185   body
186   sender
187 }
188 
189 subscription sub {
190   newMessage {
191     ... newMessageFields  
192   }
193 
194 }`);
195 
196 	tests ~= TestCase(11, `
197 query HeroNameAndFriends($episode: Episode) {
198   hero(episode: $episode) {
199     name,
200     friends {
201       name
202     }
203   }
204 }`);
205 
206 	tests ~= TestCase(12, `
207 query name {
208   leftComparison: hero(episode: $EMPIRE) {
209     ...comparisonFields
210   }
211   rightComparison: hero(episode: $JEDI) {
212     ...comparisonFields
213   }
214 }
215 fragment comparisonFields on Character {
216   name
217   appearsIn
218   friends {
219     name
220   }
221 }
222 
223 `);
224 
225 	tests ~= TestCase(13, `
226 query name{
227  builds(first: 54) {
228 	all: number
229  }
230 }
231 `);
232 
233 	tests ~= TestCase(14, `
234 		query foo {
235 			viewer {
236 				user {
237 					name
238 						builds(first: 1) {
239 							edges {
240 								node {
241 									number
242 										branch
243 										message
244 								}
245 							}
246 						}
247 				}
248 			}
249 		}
250 `);
251 
252 	tests ~= TestCase(15, `
253 query foo {
254 	name
255     builds(first: 1) {
256 		abc
257   }
258  }`);
259 
260 	tests ~= TestCase(16, `
261 query h {
262 	name
263     builds
264   }
265 `);
266 
267 	tests ~= TestCase(17, `
268   query human($id: H, $limit: lim, $gender: Gen) {
269     name
270     height
271 	friends {
272 		id
273 		gender
274 		income
275 	}
276   }` );
277 
278 	tests ~= TestCase(18, `
279   query human($id: Var) {
280     name
281     height
282   }`);
283 
284 	tests ~= TestCase(19, `
285   query human() {
286     name
287     height
288   }`);
289 
290 	tests ~= TestCase(20, `
291   query name {
292 	  foo
293 }`); 
294 
295 	/*tests ~= TestCase(21, `{
296 		query n {
297   __type(name: "User") {
298     name
299     fields {
300       name
301       type {
302         name
303       }
304     }
305   }
306 }
307 }`);*/
308 
309 	tests ~= TestCase(21, `{
310  user(id: 1) {
311    friends {
312      name
313    }
314  }
315 }`);
316 
317 	tests ~= TestCase(22, `query IntrospectionQueryTypeQuery {
318           __schema {
319             queryType {
320               fields {
321                 name
322                 args {
323                   name
324                   description
325                   type {
326                     name
327                     kind
328                     ofType {
329                       name
330                       kind
331                     }
332                   }
333                   defaultValue
334                 }
335               }
336             }
337           }
338         }`);
339 
340 	tests ~= TestCase(23, `query IntrospectionDroidFieldsQuery {
341           __type(name: "Droid") {
342             name
343             fields {
344               name
345               type {
346                 name
347                 kind
348               }
349             }
350           }
351         }`);
352 
353 	tests ~= TestCase(24, `
354         query IntrospectionCharacterKindQuery {
355           __type(name: "Character") {
356             name
357             kind
358           }
359         }`);
360 
361 
362 	tests ~= TestCase(25, `query IntrospectionDroidKindQuery {
363           __type(name: "Droid") {
364             name
365             kind
366           }
367         }`);
368 
369 	tests ~= TestCase(26, `query IntrospectionDroidTypeQuery {
370           __type(name: "Droid") {
371             name
372           }
373         }`);
374 
375 	tests ~= TestCase(27, `query IntrospectionQueryTypeQuery {
376           __schema {
377             queryType {
378               name
379             }
380           }
381         }`);
382 
383 	tests ~= TestCase(28, `query IntrospectionTypeQuery {
384           __schema {
385             types {
386               name
387             }
388           }
389         }`);
390 
391 	tests ~= TestCase(29, `query IntrospectionDroidDescriptionQuery {
392           __type(name: "Droid") {
393             name
394             description
395           }
396         }`);
397 
398 	foreach(test; tests) {
399 		auto l = Lexer(test.str);
400 		auto p = Parser(l);
401 		try {
402 			auto d = p.parseDocument();
403 		} catch(Throwable e) {
404 			writeln(e.toString());
405 			while(e.next) {
406 				writeln(e.next.toString());
407 				e = e.next;
408 			}
409 			assert(false, format("Test %d", test.id));
410 		}
411 		assert(p.lex.empty, format("%d", test.id));
412 	}
413 }