/** * This file is part of Graylog. * * Graylog is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Graylog is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Graylog. If not, see <http://www.gnu.org/licenses/>. */ package integration; import com.google.common.base.CaseFormat; import com.google.common.collect.Sets; import com.google.common.io.Resources; import com.jayway.restassured.matcher.ResponseAwareMatcher; import com.jayway.restassured.response.Response; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.core.IsCollectionContaining; import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; import java.util.Collections; import java.util.Map; import java.util.Set; import static org.hamcrest.core.IsNot.not; public class BaseRestTestHelper { /** * Returns a {@link com.jayway.restassured.matcher.ResponseAwareMatcher} which checks that all given keys are present, * and that no additional keys are in the given path. * Given this JSON * <pre> * { * "version": "2.0.0", * "codename": "foo" * } * </pre> * to validate that it contains the keys <code>version</code> and <code>codename</code> and only those keys, use * <pre> * given() * .when() * .get("/") * .then() * .body(".", containsAllKeys("codename", "version")); * </pre> * If any of the keys are missing, or there are other keys in the JSON document, the matcher will fail. * @param keys the keys that need to be present * @return matcher */ public static ResponseAwareMatcher<Response> containsAllKeys(String... keys) { return new StrictKeysPresentMatcher(keys); } /** * Returns a {@link com.jayway.restassured.matcher.ResponseAwareMatcher} which checks that the given keys are present. * Given this JSON * <pre> * { * "version": "2.0.0", * "codename": "foo" * } * </pre> * to validate that it contains the key <code>version</code>, use * <pre> * given() * .when() * .get("/") * .then() * .body(".", containsKeys("version")); * </pre> * If any of the keys are missing from the JSON document, the matcher will fail. * @param keys the keys that need to be present * @return matcher */ public static ResponseAwareMatcher<Response> containsKeys(String... keys) { return new KeysPresentMatcher(keys); } public static ResponseAwareMatcher<Response> setContainsEntry(final String entry) { return response -> IsCollectionContaining.hasItem(entry); } public static ResponseAwareMatcher<Response> setDoesNotContain(final String entry) { return response -> not(IsCollectionContaining.hasItem(entry)); } /** * Use this method to load a json file from the classpath. * <p> * It will be looked for relative to the caller's object's class. * </p> * For example if you have a class in the package <code>integration.system.users</code> and call <code>jsonResource("abc.json")</code> * on the instance of your test class (i.e. <code>this</code>), it will look for a file in <code>resources/integration/system/inputs/abc.json</code>. * <p> * If the file does not exist or cannot be read, a {@link java.lang.IllegalStateException} will be raised which should cause your test to fail. * </p> * @param relativeFileName the name of the file relative to the caller's class. * @return the bytes in that file. */ protected String jsonResource(String relativeFileName) { final URL resource = Resources.getResource(this.getClass(), relativeFileName); if (resource == null) { throw new IllegalStateException("Unable to find JSON resource " + relativeFileName + " for test. This is a bug."); } try { return Resources.toString(resource, Charset.defaultCharset()); } catch (IOException e) { throw new IllegalStateException("Unable to read JSON resource " + relativeFileName + " for test. This is a bug."); } } /** * Same as @{link #jsonResource} but guesses the file name from the caller's method name. Be careful when refactoring. * @return the bytes in that file */ protected String jsonResourceForMethod() { final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); final String testMethodName = stackTraceElements[2].getMethodName(); final String filename = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, testMethodName); return jsonResource(filename + ".json"); } private static class KeysPresentMatcher implements ResponseAwareMatcher<Response> { private final Set<String> keys = Sets.newHashSet(); public KeysPresentMatcher(String... keys) { Collections.addAll(this.keys, keys); } @Override public Matcher<?> matcher(Response response) throws Exception { return new BaseMatcher<Response>() { private Sets.SetView difference; @Override public boolean matches(Object item) { if (item instanceof Map) { final Set keySet = ((Map) item).keySet(); difference = Sets.difference(keys, keySet); return difference.isEmpty(); } return false; } @Override public void describeTo(Description description) { description.appendText("JSON contains keys: ").appendValueList("[", ", ", "]", keys); } @Override public void describeMismatch(Object item, Description description) { super.describeMismatch(item, description); description.appendValueList(" has extra or missing keys: [", ", ", "]", difference); } }; } } private static class StrictKeysPresentMatcher implements ResponseAwareMatcher<Response> { private final Set<String> keys = Sets.newHashSet(); public StrictKeysPresentMatcher(String... keys) { Collections.addAll(this.keys, keys); } @Override public Matcher<?> matcher(Response response) throws Exception { return new BaseMatcher<Response>() { private Sets.SetView difference; @Override public boolean matches(Object item) { if (item instanceof Map) { @SuppressWarnings("unchecked") final Set<? extends String> keySet = ((Map) item).keySet(); difference = Sets.symmetricDifference(keySet, keys); return difference.isEmpty(); } return false; } @Override public void describeTo(Description description) { description.appendText("JSON Contains all keys: ").appendValueList("[", ", ", "]", keys); } @Override public void describeMismatch(Object item, Description description) { super.describeMismatch(item, description); description.appendValueList(" has extra or missing keys: [", ", ", "]", difference); } }; } } }