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 }