View Javadoc
1   package net.avcompris.commons3.api.tests;
2   
3   import static com.google.common.base.Preconditions.checkNotNull;
4   import static org.apache.commons.lang3.StringUtils.substringAfter;
5   import static org.apache.commons.lang3.StringUtils.substringBefore;
6   
7   import java.io.File;
8   import java.io.FileInputStream;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.util.ArrayList;
12  import java.util.List;
13  
14  import javax.annotation.Nullable;
15  
16  import org.apache.commons.lang3.NotImplementedException;
17  
18  import com.google.common.collect.Iterables;
19  
20  import net.avcompris.commons3.yaml.Yaml;
21  import net.avcompris.commons3.yaml.YamlUtils;
22  
23  public final class TestsSpec {
24  
25  	private final List<TestSpec> tests = new ArrayList<>();
26  
27  	private TestsSpec(final Yaml yaml, final Class<?>[] classes) {
28  
29  		checkNotNull(yaml, "yaml");
30  		checkNotNull(classes, "classes");
31  
32  		final Yaml tests = yaml.get("tests");
33  
34  		for (final Object key : tests.keys()) {
35  
36  			final String testName = (String) key;
37  
38  			final Yaml test = tests.get(testName);
39  
40  			final AuthenticationSpec authenticationSpec;
41  
42  			if (test.has("authentified")) {
43  
44  				final String authentified = test.get("authentified").asString();
45  
46  				if ("false".contentEquals(authentified)) {
47  
48  					authenticationSpec = null;
49  
50  				} else if ("true".contentEquals(authentified)) {
51  
52  					authenticationSpec = new AuthenticationSpec() {
53  
54  					};
55  
56  				} else {
57  
58  					throw new NotImplementedException("authentified: " + authentified);
59  				}
60  
61  			} else {
62  
63  				throw new NotImplementedException("YAML has no \"authentified\" section");
64  			}
65  
66  			final List<Step> steps = new ArrayList<>();
67  
68  			if (test.has("request")) {
69  
70  				steps.add(loadStep(authenticationSpec, test, classes));
71  
72  			} else if (test.has("steps")) {
73  
74  				for (final Yaml step : test.get("steps").items()) {
75  
76  					steps.add(loadStep(authenticationSpec, step, classes));
77  				}
78  
79  			} else {
80  
81  				throw new NotImplementedException("");
82  			}
83  
84  			this.tests.add(new TestSpec() {
85  
86  				@Override
87  				public String getName() {
88  					return testName;
89  				}
90  
91  				@Override
92  				@Nullable
93  				public AuthenticationSpec getAuthentication() {
94  					return authenticationSpec;
95  				}
96  
97  				@Override
98  				public Step[] getSteps() {
99  					return Iterables.toArray(steps, Step.class);
100 				}
101 
102 				@Override
103 				public String toString() {
104 					return testName;
105 				}
106 			});
107 		}
108 	}
109 
110 	public static TestsSpec load(final File yamlFile, final Class<?>... classes) throws IOException {
111 
112 		checkNotNull(yamlFile, "yamlFile");
113 
114 		try (InputStream is = new FileInputStream(yamlFile)) {
115 
116 			return load(is, classes);
117 		}
118 	}
119 
120 	public static TestsSpec load(final InputStream is, final Class<?>... classes) throws IOException {
121 
122 		checkNotNull(is, "is");
123 
124 		final Yaml yaml = YamlUtils.loadYaml(is);
125 
126 		return new TestsSpec(yaml, classes);
127 	}
128 
129 	public TestSpec[] getTests() {
130 
131 		return Iterables.toArray(tests, TestSpec.class);
132 	}
133 
134 	public interface TestSpec {
135 
136 		String getName();
137 
138 		@Nullable
139 		AuthenticationSpec getAuthentication();
140 
141 		Step[] getSteps();
142 	}
143 
144 	public interface AuthenticationSpec {
145 
146 	}
147 
148 	public enum HttpMethod {
149 
150 		GET, POST, PUT, DELETE,
151 	}
152 
153 	public interface Step {
154 
155 		@Nullable
156 		AuthenticationSpec getAuthentication();
157 
158 		HttpMethod getHttpMethod();
159 
160 		String getPath();
161 
162 		Data[] getData();
163 
164 		@Nullable
165 		Class<?> getReturnType();
166 
167 		Assertion[] getAssertions();
168 
169 		Let[] getLets();
170 	}
171 
172 	public interface Data {
173 
174 		String getName();
175 
176 		Object getValue();
177 	}
178 
179 	public enum AssertionType {
180 
181 		EQUALS,
182 	}
183 
184 	public interface Assertion {
185 
186 		AssertionType getType();
187 
188 		String getLeft();
189 
190 		String getRight();
191 	}
192 
193 	public interface Let {
194 
195 		String getVariableName();
196 
197 		String getExpression();
198 	}
199 
200 	private static Class<?> extractClass(final String classSimpleName, final Class<?>[] classes) {
201 
202 		for (final Class<?> clazz : classes) {
203 
204 			if (clazz.getSimpleName().contentEquals(classSimpleName)) {
205 
206 				return clazz;
207 			}
208 		}
209 
210 		throw new RuntimeException("Unknown class references in YAML: " + classSimpleName);
211 	}
212 
213 	private static Step loadStep(@Nullable final AuthenticationSpec authenticationSpec, final Yaml step,
214 			final Class<?>[] classes) {
215 
216 		final String request = step.get("request").asString();
217 
218 		final Yaml response = step.get("response");
219 
220 		final String responseStatus;
221 		final Class<?> returnType;
222 
223 		if (response.has("status")) {
224 
225 			responseStatus = response.get("status").asString();
226 
227 		} else {
228 
229 			responseStatus = response.asString();
230 		}
231 
232 		if (response.has("class")) {
233 
234 			returnType = extractClass(response.get("class").asString(), classes);
235 
236 		} else {
237 
238 			returnType = null;
239 		}
240 
241 		final HttpMethod method = HttpMethod.valueOf(substringBefore(request, " ").trim());
242 
243 		final String path = substringAfter(request, " ").trim();
244 
245 		final List<Data> data = new ArrayList<>();
246 
247 		if (step.has("data")) {
248 
249 			for (final Object key : step.get("data").keys()) {
250 
251 				final String name = (String) key;
252 
253 				final Object value;
254 
255 				final Yaml yaml = step.get("data").get(name);
256 
257 				if (yaml.isArray()) {
258 
259 					final List<String> strings = new ArrayList<>();
260 
261 					for (final Yaml item : yaml.items()) {
262 
263 						strings.add(item.asString());
264 					}
265 
266 					value = Iterables.toArray(strings, String.class);
267 
268 				} else {
269 
270 					value = yaml.asString();
271 				}
272 
273 				data.add(new Data() {
274 
275 					@Override
276 					public String getName() {
277 						return name;
278 					}
279 
280 					@Override
281 					public Object getValue() {
282 						return value;
283 					}
284 				});
285 			}
286 		}
287 
288 		final List<Assertion> assertions = new ArrayList<>();
289 
290 		assertions.add(new Assertion() {
291 
292 			@Override
293 			public AssertionType getType() {
294 				return AssertionType.EQUALS;
295 			}
296 
297 			@Override
298 			public String getLeft() {
299 				return "response.status";
300 			}
301 
302 			@Override
303 			public String getRight() {
304 				return responseStatus;
305 			}
306 		});
307 
308 		if (step.has("assertions")) {
309 
310 			for (final Yaml item : step.get("assertions").items()) {
311 
312 				final String expression = item.asString();
313 
314 				if (!expression.contains("==")) {
315 
316 					throw new NotImplementedException("expession: " + expression);
317 				}
318 
319 				final String left = substringBefore(expression, "==").trim();
320 				final String right = substringAfter(expression, "==").trim();
321 
322 				assertions.add(new Assertion() {
323 
324 					@Override
325 					public AssertionType getType() {
326 						return AssertionType.EQUALS;
327 					}
328 
329 					@Override
330 					public String getLeft() {
331 						return left;
332 					}
333 
334 					@Override
335 					public String getRight() {
336 						return right;
337 					}
338 				});
339 			}
340 		}
341 
342 		final List<Let> lets = new ArrayList<>();
343 
344 		if (step.has("let")) {
345 
346 			final Yaml let = step.get("let");
347 
348 			for (final Object key : let.keys()) {
349 
350 				final String variableName = (String) key;
351 
352 				final String expression = let.get(variableName).asString();
353 
354 				lets.add(new Let() {
355 
356 					@Override
357 					public String getVariableName() {
358 						return variableName;
359 					}
360 
361 					@Override
362 					public String getExpression() {
363 						return expression;
364 					}
365 				});
366 			}
367 		}
368 
369 		return new Step() {
370 
371 			@Override
372 			@Nullable
373 			public AuthenticationSpec getAuthentication() {
374 				return authenticationSpec;
375 			}
376 
377 			@Override
378 			public HttpMethod getHttpMethod() {
379 				return method;
380 			}
381 
382 			@Override
383 			public String getPath() {
384 				return path;
385 			}
386 
387 			@Override
388 			public Data[] getData() {
389 				return Iterables.toArray(data, Data.class);
390 			}
391 
392 			@Override
393 			@Nullable
394 			public Class<?> getReturnType() {
395 				return returnType;
396 			}
397 
398 			@Override
399 			public Assertion[] getAssertions() {
400 				return Iterables.toArray(assertions, Assertion.class);
401 			}
402 
403 			@Override
404 			public Let[] getLets() {
405 				return Iterables.toArray(lets, Let.class);
406 			}
407 		};
408 	}
409 }