/* LanguageTool, a natural language style checker
* Copyright (C) 2006 Daniel Naber (http://www.danielnaber.de)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package org.languagetool.server;
import org.apache.commons.lang3.StringUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.languagetool.Language;
import org.languagetool.language.*;
import org.languagetool.tools.StringTools;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashSet;
import static org.junit.Assert.*;
public class HTTPServerTest {
private static final int MAX_LENGTH = 50_000; // needs to be in sync with server conf!
@Ignore("already gets tested by sub class HTTPServerLoadTest")
@Test
public void testHTTPServer() throws Exception {
HTTPServer server = new HTTPServer();
assertFalse(server.isRunning());
try {
server.run();
assertTrue(server.isRunning());
runTestsV2();
} finally {
server.stop();
assertFalse(server.isRunning());
}
}
void runTestsV2() throws IOException, SAXException, ParserConfigurationException {
// no error:
String emptyResultPattern = ".*\"matches\":\\[\\].*";
German german = new German();
String result1 = checkV2(german, "");
assertTrue("Got " + result1 + ", expected " + emptyResultPattern, result1.matches(emptyResultPattern));
String result2 = checkV2(german, "Ein kleiner test");
assertTrue("Got " + result2 + ", expected " + emptyResultPattern, result2.matches(emptyResultPattern));
// one error:
assertTrue(checkV2(german, "ein kleiner test.").contains("UPPERCASE_SENTENCE_START"));
// two errors:
String result = checkV2(german, "ein kleiner test. Und wieder Erwarten noch was: \u00f6\u00e4\u00fc\u00df.");
assertTrue("Got result without 'UPPERCASE_SENTENCE_START': " + result, result.contains("UPPERCASE_SENTENCE_START"));
assertTrue("Got result without 'WIEDER_WILLEN': " + result, result.contains("WIEDER_WILLEN"));
assertTrue("Expected special chars, got: '" + result + "'",
result.contains("\u00f6\u00e4\u00fc\u00df")); // special chars are intact
assertTrue(checkV2(german, "bla <script>").contains("<script>")); // no escaping of '<' and '>' needed, unlike in XML
// other tests for special characters
String germanSpecialChars = checkV2(german, "ein kleiner test. Und wieder Erwarten noch was: öäüß+ öäüß.");
assertTrue("Expected special chars, got: '" + germanSpecialChars + "'", germanSpecialChars.contains("öäüß+"));
String romanianSpecialChars = checkV2(new Romanian(), "bla bla șțîâă șțîâă și câteva caractere speciale");
assertTrue("Expected special chars, got: '" + romanianSpecialChars + "'", romanianSpecialChars.contains("șțîâă"));
Polish polish = new Polish();
String polishSpecialChars = checkV2(polish, "Mówiła długo, żeby tylko mówić mówić długo.");
assertTrue("Expected special chars, got: '" + polishSpecialChars+ "'", polishSpecialChars.contains("mówić"));
// test http POST
assertTrue(checkByPOST(new Romanian(), "greșit greșit").contains("greșit"));
// test supported language listing
URL url = new URL("http://localhost:" + HTTPTools.getDefaultPort() + "/v2/languages");
String languagesJson = StringTools.streamToString((InputStream) url.getContent(), "UTF-8");
if (!languagesJson.contains("Romanian") || !languagesJson.contains("English")) {
fail("Error getting supported languages: " + languagesJson);
}
if (!languagesJson.contains("\"de\"") || !languagesJson.contains("\"de-DE\"")) {
fail("Error getting supported languages: " + languagesJson);
}
// tests for "&" character
English english = new English();
assertTrue(checkV2(english, "Me & you you").contains("&"));
// tests for mother tongue (copy from link {@link FalseFriendRuleTest})
assertTrue(checkV2(english, german, "We will berate you").contains("BERATE"));
assertTrue(checkV2(german, english, "Man sollte ihn nicht so beraten.").contains("BERATE"));
assertTrue(checkV2(polish, english, "To jest frywolne.").contains("FRIVOLOUS"));
//test for no changed if no options set
String[] nothing = {};
assertEquals(checkV2(english, german, "We will berate you"),
checkWithOptionsV2(english, german, "We will berate you", nothing, nothing, false));
//disabling
String[] disableAvsAn = {"EN_A_VS_AN"};
assertTrue(!checkWithOptionsV2(
english, german, "This is an test", nothing, disableAvsAn, false).contains("an test"));
//enabling
assertTrue(checkWithOptionsV2(
english, german, "This is an test", disableAvsAn, nothing, false).contains("an test"));
//should also mean _NOT_ disabling all other rules...
assertTrue(checkWithOptionsV2(
english, german, "We will berate you", disableAvsAn, nothing, false).contains("BERATE"));
//..unless explicitly stated.
assertTrue(!checkWithOptionsV2(
english, german, "We will berate you", disableAvsAn, nothing, true).contains("BERATE"));
//test if two rules get enabled as well
String[] twoRules = {"EN_A_VS_AN", "BERATE"};
String resultEn = checkWithOptionsV2(
english, german, "This is an test. We will berate you.", twoRules, nothing, false);
assertTrue("Result: " + resultEn, resultEn.contains("EN_A_VS_AN"));
assertTrue("Result: " + resultEn, resultEn.contains("BERATE"));
//check two disabled options
String result3 = checkWithOptionsV2(
english, german, "This is an test. We will berate you.", nothing, twoRules, false);
assertFalse("Result: " + result3, result3.contains("EN_A_VS_AN"));
assertFalse("Result: " + result3, result3.contains("BERATE"));
//two disabled, one enabled, so enabled wins
String result4 = checkWithOptionsV2(
english, german, "This is an test. We will berate you.", disableAvsAn, twoRules, false);
assertTrue("Result: " + result4, result4.contains("EN_A_VS_AN"));
assertFalse("Result: " + result4, result4.contains("BERATE"));
String result5 = checkV2(null, "This is a test of the language detection.");
assertTrue("Result: " + result5, result5.contains("\"en-US\""));
String result6 = checkV2(null, "This is a test of the language detection.", "&preferredVariants=de-DE,en-GB");
assertTrue("Result: " + result6, result6.contains("\"en-GB\""));
String result7 = checkV2(null, "x"); // too short for auto-fallback, will use fallback
assertTrue("Result: " + result7, result7.contains("\"en-US\""));
}
@Test
public void testTimeout() throws Exception {
HTTPServerConfig config = new HTTPServerConfig(HTTPTools.getDefaultPort(), false);
config.setMaxCheckTimeMillis(1);
HTTPServer server = new HTTPServer(config, false);
try {
server.run();
try {
System.out.println("=== Testing timeout now, please ignore the following exception ===");
checkV2(new GermanyGerman(), "Einq Tesz miit fieln Fehlan, desshalb sehee laagnsam bee dr Rechtschriebpürfung");
fail("Check was expected to be stopped because it took too long");
} catch (IOException expected) {
if (!expected.toString().contains(" 503 ")) {
fail("Expected exception with error 503, got: " + expected);
}
}
} finally {
server.stop();
}
}
@Test
public void testAccessDenied() throws Exception {
HTTPServer server = new HTTPServer(new HTTPServerConfig(HTTPTools.getDefaultPort()), false, new HashSet<>());
try {
server.run();
try {
System.out.println("=== Testing 'access denied' check now, please ignore the following exception ===");
checkV1(new German(), "no ip address allowed, so this cannot work");
fail();
} catch (IOException expected) {
if (!expected.toString().contains(" 403 ")) {
fail("Expected exception with error 403, got: " + expected);
}
}
try {
System.out.println("=== Testing 'access denied' check now, please ignore the following exception ===");
checkV2(new German(), "no ip address allowed, so this cannot work");
fail();
} catch (IOException expected) {
if (!expected.toString().contains(" 403 ")) {
fail("Expected exception with error 403, got: " + expected);
}
}
} finally {
server.stop();
}
}
@Test
public void testEnabledOnlyParameter() throws Exception {
HTTPServer server = new HTTPServer(new HTTPServerConfig(HTTPTools.getDefaultPort()), false);
try {
server.run();
try {
System.out.println("=== Testing 'enabledOnly parameter' now, please ignore the following exception ===");
URL url = new URL("http://localhost:" + HTTPTools.getDefaultPort() + "/?text=foo&language=en-US&disabled=EN_A_VS_AN&enabledOnly=yes");
HTTPTools.checkAtUrl(url);
fail();
} catch (IOException expected) {
if (!expected.toString().contains(" 400 ")) {
fail("Expected exception with error 400, got: " + expected);
}
}
} finally {
server.stop();
}
}
@Test
public void testMissingLanguageParameter() throws Exception {
HTTPServer server = new HTTPServer(new HTTPServerConfig(HTTPTools.getDefaultPort()), false);
try {
server.run();
try {
System.out.println("=== Testing 'missing language parameter' now, please ignore the following exception ===");
URL url = new URL("http://localhost:" + HTTPTools.getDefaultPort() + "/?text=foo");
HTTPTools.checkAtUrl(url);
fail();
} catch (IOException expected) {
if (!expected.toString().contains(" 400 ")) {
fail("Expected exception with error 400, got: " + expected);
}
}
} finally {
server.stop();
}
}
private String checkV1(Language lang, String text) throws IOException {
return checkV1(lang, null, text);
}
private String checkV2(Language lang, String text) throws IOException {
return checkV2(lang, (Language)null, text);
}
protected String checkV1(Language lang, Language motherTongue, String text) throws IOException {
return check("/", lang, motherTongue, text, "");
}
protected String checkV2(Language lang, Language motherTongue, String text) throws IOException {
return check("/v2/check", lang, motherTongue, text, "");
}
private String checkV2(Language lang, String text, String parameters) throws IOException {
return check("/v2/check", lang, null, text, parameters);
}
private String check(String urlPrefix, Language lang, Language motherTongue, String text, String parameters) throws IOException {
String urlOptions = urlPrefix + "?language=" + (lang == null ? "auto" : lang.getShortCode());
urlOptions += "&disabledRules=HUNSPELL_RULE&text=" + URLEncoder.encode(text, "UTF-8"); // latin1 is not enough for languages like polish, romanian, etc
if (motherTongue != null) {
urlOptions += "&motherTongue=" + motherTongue.getShortCode();
}
urlOptions += parameters;
URL url = new URL("http://localhost:" + HTTPTools.getDefaultPort() + urlOptions);
return HTTPTools.checkAtUrl(url);
}
private String checkWithOptionsV2(Language lang, Language motherTongue, String text,
String[] enabledRules, String[] disabledRules, boolean useEnabledOnly) throws IOException {
String urlOptions = "/v2/check?language=" + lang.getShortCode();
urlOptions += "&text=" + URLEncoder.encode(text, "UTF-8"); // latin1 is not enough for languages like polish, romanian, etc
if (motherTongue != null) {
urlOptions += "&motherTongue=" + motherTongue.getShortCode();
}
if (disabledRules.length > 0) {
urlOptions += "&disabledRules=" + StringUtils.join(disabledRules, ",");
}
if (enabledRules.length > 0) {
urlOptions += "&enabledRules=" + StringUtils.join(enabledRules, ",");
}
if (useEnabledOnly) {
urlOptions += "&enabledOnly=yes";
}
URL url = new URL("http://localhost:" + HTTPTools.getDefaultPort() + urlOptions);
return HTTPTools.checkAtUrl(url);
}
/**
* Same as {@link #checkV1(Language, String)} but using HTTP POST method instead of GET
*/
protected String checkByPOST(Language lang, String text) throws IOException {
String postData = "language=" + lang.getShortCodeWithCountryAndVariant() + "&text=" + URLEncoder.encode(text, "UTF-8"); // latin1 is not enough for languages like Polish, Romanian, etc
URL url = new URL("http://localhost:" + HTTPTools.getDefaultPort() + "/v2/check");
try {
return HTTPTools.checkAtUrlByPost(url, postData);
} catch (IOException e) {
if (text.length() > MAX_LENGTH) {
// this is expected, log it anyway:
System.err.println("Got expected error on long text (" + text.length() + " chars): " + e.getMessage());
return "";
} else {
System.err.println("Got error from " + url + " (" + lang.getShortCodeWithCountryAndVariant() + ", " +
text.length() + " chars): " + e.getMessage() + ", text was (" + text.length() + " chars): '" + StringUtils.abbreviate(text, 100) + "'");
return "";
}
}
}
}