package org.rapidoid.http;
/*
* #%L
* rapidoid-integration-tests
* %%
* Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.junit.After;
import org.junit.Before;
import org.rapidoid.RapidoidModules;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.commons.Arr;
import org.rapidoid.commons.Str;
import org.rapidoid.config.Conf;
import org.rapidoid.crypto.Crypto;
import org.rapidoid.data.JSON;
import org.rapidoid.env.Env;
import org.rapidoid.fluent.Do;
import org.rapidoid.io.IO;
import org.rapidoid.jpa.JPA;
import org.rapidoid.jpa.JPAUtil;
import org.rapidoid.lambda.F3;
import org.rapidoid.log.Log;
import org.rapidoid.reverseproxy.Reverse;
import org.rapidoid.scan.ClasspathUtil;
import org.rapidoid.setup.Admin;
import org.rapidoid.setup.App;
import org.rapidoid.setup.My;
import org.rapidoid.setup.On;
import org.rapidoid.test.RapidoidIntegrationTest;
import org.rapidoid.test.TestCommons;
import org.rapidoid.u.U;
import org.rapidoid.util.Msc;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@Authors("Nikolche Mihajlovski")
@Since("5.2.5")
public abstract class IsolatedIntegrationTest extends TestCommons {
// FIXME HEAD
private static final List<String> HTTP_VERBS = U.list("GET", "DELETE", "OPTIONS", "TRACE", "POST", "PUT", "PATCH");
public static final int DEFAULT_PORT = 8080;
public static final String LOCALHOST = Msc.http() + "://localhost:8080";
@Before
public void openContext() {
TimeZone.setDefault(TimeZone.getTimeZone("CET"));
ClasspathUtil.setRootPackage("some.nonexisting.app");
RapidoidModules.getAll(); // all modules must be present
RapidoidIntegrationTest.before(this);
JPAUtil.reset();
Conf.ROOT.setPath(getTestNamespace());
U.print("--- STARTING SERVER ---");
My.reset();
App.resetGlobalState();
On.changes().ignore();
On.setup().activate();
On.setup().reload();
App.path(getTestPackageName());
U.print("--- SERVER STARTED ---");
verifyNoRoutes();
U.must(Msc.isInsideTest());
U.must(Env.test());
Env.reset();
Conf.reset();
Conf.ROOT.setPath(getTestNamespace());
U.must(Msc.isInsideTest());
U.must(Env.test());
RapidoidIntegrationTest.start(this);
}
@After
public void closeContext() {
U.print("--- STOPPING SERVER ---");
if (Admin.setup().isRunning()) {
if (Admin.setup().port() == On.setup().port()) {
Admin.setup().reset();
} else {
Admin.setup().shutdown();
}
}
U.print("--- SERVER STOPPED ---");
RapidoidIntegrationTest.after(this);
}
protected String localhost(String uri) {
return localhost(DEFAULT_PORT, uri);
}
protected String localhost(int port, String uri) {
return Msc.http() + "://localhost:" + port + uri;
}
protected void defaultServerSetup() {
On.get("/echo").serve((Req x) -> {
x.response().contentType(MediaType.PLAIN_TEXT_UTF_8);
return x.verb() + ":" + x.path() + ":" + x.query();
});
On.get("/hello").html("Hello");
On.post("/upload").plain((Req x) -> {
Log.info("Uploaded files", "files", x.files().keySet());
boolean hasF3 = x.files().containsKey("f3");
return U.join(":", x.cookies().get("foo"), x.cookies().get("COOKIE1"), x.posted().get("a"), x.files().size(),
Crypto.md5(x.file("f1").content()),
Crypto.md5(x.files().get("f2").get(0).content()),
Crypto.md5(hasF3 ? x.file("f3").content() : new byte[0]));
});
On.req((Req x) -> x.response().html(U.join(":", x.verb(), x.path(), x.query())));
}
protected String get(String uri) {
return HTTP.get(localhost(uri)).fetch();
}
protected byte[] getBytes(String uri) {
return HTTP.get(localhost(uri)).execute().bodyBytes();
}
protected void onlyGet(String uri) {
onlyGet(DEFAULT_PORT, uri);
}
protected void onlyGet(int port, String uri) {
onlyReq(port, "GET", uri, null, null);
}
protected void onlyPost(String uri) {
onlyPost(DEFAULT_PORT, uri, null);
}
protected void onlyPost(String uri, Map<String, ?> data) {
onlyPost(DEFAULT_PORT, uri, data);
}
protected void onlyPost(int port, String uri, Map<String, ?> data) {
onlyReq(port, "POST", uri, data, null);
}
protected void onlyPost(String uri, String json) {
onlyReq(DEFAULT_PORT, "POST", uri, null, json);
}
protected void onlyPut(String uri) {
onlyPut(DEFAULT_PORT, uri, null);
}
protected void onlyPut(int port, String uri, Map<String, ?> data) {
onlyReq(port, "PUT", uri, data, null);
}
protected void onlyPut(String uri, Map<String, ?> data) {
onlyPut(DEFAULT_PORT, uri, data);
}
protected void onlyPut(String uri, String json) {
onlyReq(DEFAULT_PORT, "PUT", uri, null, json);
}
protected void onlyDelete(String uri) {
onlyDelete(DEFAULT_PORT, uri);
}
protected void onlyDelete(int port, String uri) {
onlyReq(port, "DELETE", uri, null, null);
}
protected void getAndPost(String uri) {
getAndPost(DEFAULT_PORT, uri);
}
protected void getAndPost(int port, String uri) {
testReq(port, "GET", uri, null, null);
testReq(port, "POST", uri, null, null);
notFoundExcept(port, uri, "GET", "POST");
}
protected void getReq(String uri) {
testReq(DEFAULT_PORT, "GET", uri, null, null);
}
protected void postData(String uri, Map<String, ?> data) {
testReq(DEFAULT_PORT, "POST", uri, data, null);
}
protected void postJson(String uri, Map<String, ?> data) {
testReq(DEFAULT_PORT, "POST", uri, null, JSON.stringify(data));
}
protected void postData(int port, String uri, Map<String, ?> data) {
testReq(port, "POST", uri, data, null);
}
protected void putData(String uri, Map<String, ?> data) {
testReq(DEFAULT_PORT, "PUT", uri, data, null);
}
protected void putData(int port, String uri, Map<String, ?> data) {
testReq(port, "PUT", uri, data, null);
}
protected void patchData(String uri, Map<String, ?> data) {
testReq(DEFAULT_PORT, "PATCH", uri, data, null);
}
protected void patchData(int port, String uri, Map<String, ?> data) {
testReq(port, "PATCH", uri, data, null);
}
private void onlyReq(int port, String verb, String uri, Map<String, ?> data, String json) {
testReq(port, verb, uri, data, json);
notFoundExcept(port, uri, verb);
}
protected void deleteReq(String uri) {
testReq(DEFAULT_PORT, "DELETE", uri, null, null);
}
protected void notFoundExcept(String uri, String... exceptVerbs) {
notFoundExcept(DEFAULT_PORT, uri, exceptVerbs);
}
protected void notFoundExcept(int port, String uri, String... exceptVerbs) {
for (String verb : HTTP_VERBS) {
if (Arr.indexOf(exceptVerbs, verb) < 0) {
notFound(port, verb, uri);
}
}
}
protected void notFound(String uri) {
notFound(DEFAULT_PORT, uri);
}
protected void notFound(int port, String uri) {
notFoundExcept(port, uri);
}
protected void notFound(int port, String verb, String uri) {
String resp = fetch(port, verb, uri, null, null);
String notFound = U.notNull(IO.load("404-not-found.txt"), "404-not-found");
String notFound2 = U.notNull(IO.load("404-not-found-json.txt"), "404-not-found-json");
if (!httpResultsMatch(resp, notFound) && !httpResultsMatch(resp, notFound2)) {
eq(resp, "!!! Expected (404 Not Found) HTTP response as HTML or JSON !!!!");
}
}
protected void testReq(int port, String verb, String uri, Map<String, ?> data, String json) {
String resp = fetch(port, verb, uri, data, json);
String reqName = reqName(port, verb, uri);
verifyCase(port + " " + verb + " " + uri, resp, reqName);
}
protected String fetch(int port, String verb, String uri, Map<String, ?> data, String json) {
HttpReq req = HTTP.verb(HttpVerb.from(verb)).url(localhost(port, uri)).data(data).body(json != null ? json.getBytes() : null);
return exec(req);
}
protected String fetch(HttpClient client, int port, String verb, String uri, Map<String, ?> data) {
return exec(client.req().verb(HttpVerb.from(verb)).url(localhost(port, uri)).data(data));
}
private String exec(HttpReq req) {
String url = req.url();
if (url.contains("#")) {
req.url(Str.cutToFirst(url, "#"));
}
req.raw(true);
String resp = new String(req.execute().raw());
resp = maskHttpResponse(resp);
req.raw(false);
return resp;
}
protected String maskHttpResponse(String resp) {
resp = resp.replaceAll("(?<=\n)Date: .*? GMT(?=\r?\n)", "Date: XXXXX GMT");
resp = resp.replaceAll("(?<=\nSet-Cookie: JSESSIONID=)[^;]+?;", "<THE-SESSION-ID>;");
resp = resp.replaceAll("(?<=\nSet-Cookie: _token=)[^;]+?;", "<THE-TOKEN>;");
resp = resp.replaceAll("(?<=\"token\":\")[^\"]+?\"", "<THE-TOKEN>\"");
return resp;
}
protected String fetch(HttpClient client, String verb, String uri, Map<String, ?> data) {
return fetch(client, DEFAULT_PORT, verb, uri, data);
}
protected String fetch(HttpClient client, String verb, String uri) {
return fetch(client, DEFAULT_PORT, verb, uri, null);
}
protected String fetch(String verb, String uri) {
return fetch(DEFAULT_PORT, verb, uri, null, null);
}
protected String fetch(String verb, String uri, Map<String, ?> data) {
return fetch(DEFAULT_PORT, verb, uri, data, null);
}
private String reqName(int port, String verb, String uri) {
String order = null;
if (uri.contains("#")) {
String[] parts = uri.split("#");
order = parts[1];
uri = parts[0];
}
String req = verb + uri.replace("/", "_").replace("?", "-");
if (port != DEFAULT_PORT) {
req = port + "__" + req;
}
if (order != null) {
req = order + "__" + req;
}
return req;
}
protected static Map<String, Object> reqResp(Req req, Resp resp) {
return U.map("verb", req.verb(), "uri", req.uri(), "data", req.data(), "code", resp.code());
}
protected String appRoutes() {
List<String> routes = Do.map(On.routes().all()).to((Route r) -> r.toString());
Collections.sort(routes);
return U.join("\n", routes);
}
protected void verifyRoutes() {
verify("routes", appRoutes());
}
protected void verifyRoutes(String name) {
verify("routes-" + name, appRoutes());
}
protected void verifyNoRoutes() {
isTrue(On.routes().all().isEmpty());
}
protected void tx(Runnable action) {
JPA.transaction(action);
}
protected void proxy(String match, String upstreams) {
Reverse.proxy(match).to(upstreams).add();
}
protected <T> T connect(F3<T, InputStream, BufferedReader, DataOutputStream> protocol) {
return Msc.connect("localhost", 8080, 1000, protocol);
}
}