diff --git a/app/build.gradle b/app/build.gradle index c4794016e..a731a9e3a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,7 @@ dependencies { compile "com.google.guava:guava:${GUAVA_VERSION}" testCompile 'junit:junit:4.12' + androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestCompile "com.android.support:support-annotations:${project.supportLibVersion}" androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' diff --git a/app/src/androidTest/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java b/app/src/androidTest/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java new file mode 100644 index 000000000..046962499 --- /dev/null +++ b/app/src/androidTest/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java @@ -0,0 +1,229 @@ +package fr.free.nrw.commons.mwapi; + +import android.os.Build; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import fr.free.nrw.commons.BuildConfig; +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/* + * XML for individual tests was captured by hand from the API sandbox - + * https://en.wikipedia.org/wiki/Special:ApiSandbox + */ +@RunWith(AndroidJUnit4.class) +public class ApacheHttpClientMediaWikiApiTest { + + private ApacheHttpClientMediaWikiApi testObject; + private MockWebServer server; + + @Before + public void setUp() throws Exception { + server = new MockWebServer(); + testObject = new ApacheHttpClientMediaWikiApi("http://localhost:" + server.getPort() + "/"); + } + + @After + public void teardown() throws IOException { + server.shutdown(); + } + + @Test + public void authCookiesAreHandled() { + assertEquals("", testObject.getAuthCookie()); + + testObject.setAuthCookie("cookie=chocolate-chip"); + + assertEquals("cookie=chocolate-chip", testObject.getAuthCookie()); + } + + @Test + public void simpleLoginWithWrongPassword() throws Exception { + server.enqueue(new MockResponse().setBody("")); + server.enqueue(new MockResponse().setBody("")); + + String result = testObject.login("foo", "bar"); + + RecordedRequest loginTokenRequest = assertBasicRequestParameters(server, "POST"); + Map body = parseBody(loginTokenRequest.getBody().readUtf8()); + assertEquals("xml", body.get("format")); + assertEquals("query", body.get("action")); + assertEquals("login", body.get("type")); + assertEquals("tokens", body.get("meta")); + + RecordedRequest loginRequest = assertBasicRequestParameters(server, "POST"); + body = parseBody(loginRequest.getBody().readUtf8()); + assertEquals("1", body.get("rememberMe")); + assertEquals("foo", body.get("username")); + assertEquals("bar", body.get("password")); + assertEquals("baz", body.get("logintoken")); + assertEquals("https://commons.wikimedia.org", body.get("loginreturnurl")); + assertEquals("xml", body.get("format")); + + assertEquals("wrongpassword", result); + } + + @Test + public void simpleLogin() throws Exception { + server.enqueue(new MockResponse().setBody("")); + server.enqueue(new MockResponse().setBody("")); + + String result = testObject.login("foo", "bar"); + + RecordedRequest loginTokenRequest = assertBasicRequestParameters(server, "POST"); + Map body = parseBody(loginTokenRequest.getBody().readUtf8()); + assertEquals("xml", body.get("format")); + assertEquals("query", body.get("action")); + assertEquals("login", body.get("type")); + assertEquals("tokens", body.get("meta")); + + RecordedRequest loginRequest = assertBasicRequestParameters(server, "POST"); + body = parseBody(loginRequest.getBody().readUtf8()); + assertEquals("1", body.get("rememberMe")); + assertEquals("foo", body.get("username")); + assertEquals("bar", body.get("password")); + assertEquals("baz", body.get("logintoken")); + assertEquals("https://commons.wikimedia.org", body.get("loginreturnurl")); + assertEquals("xml", body.get("format")); + + assertEquals("PASS", result); + } + + @Test + public void twoFactorLogin() throws Exception { + server.enqueue(new MockResponse().setBody("")); + server.enqueue(new MockResponse().setBody("")); + + String result = testObject.login("foo", "bar", "2fa"); + + RecordedRequest loginTokenRequest = assertBasicRequestParameters(server, "POST"); + Map body = parseBody(loginTokenRequest.getBody().readUtf8()); + assertEquals("xml", body.get("format")); + assertEquals("query", body.get("action")); + assertEquals("login", body.get("type")); + assertEquals("tokens", body.get("meta")); + + RecordedRequest loginRequest = assertBasicRequestParameters(server, "POST"); + body = parseBody(loginRequest.getBody().readUtf8()); + assertEquals("1", body.get("rememberMe")); + assertEquals("foo", body.get("username")); + assertEquals("bar", body.get("password")); + assertEquals("baz", body.get("logintoken")); + assertEquals("1", body.get("logincontinue")); + assertEquals("2fa", body.get("OATHToken")); + assertEquals("xml", body.get("format")); + + assertEquals("PASS", result); + } + + @Test + public void validateLoginForLoggedInUser() throws Exception { + server.enqueue(new MockResponse().setBody("")); + + boolean result = testObject.validateLogin(); + + RecordedRequest loginTokenRequest = assertBasicRequestParameters(server, "GET"); + Map body = parseQueryParams(loginTokenRequest); + assertEquals("xml", body.get("format")); + assertEquals("query", body.get("action")); + assertEquals("userinfo", body.get("meta")); + + assertTrue(result); + } + + @Test + public void validateLoginForLoggedOutUser() throws Exception { + server.enqueue(new MockResponse().setBody("")); + + boolean result = testObject.validateLogin(); + + RecordedRequest loginTokenRequest = assertBasicRequestParameters(server, "GET"); + Map params = parseQueryParams(loginTokenRequest); + assertEquals("xml", params.get("format")); + assertEquals("query", params.get("action")); + assertEquals("userinfo", params.get("meta")); + + assertFalse(result); + } + + @Test + public void editToken() throws Exception{ + server.enqueue(new MockResponse().setBody("")); + + String result = testObject.getEditToken(); + + RecordedRequest loginTokenRequest = assertBasicRequestParameters(server, "GET"); + Map params = parseQueryParams(loginTokenRequest); + assertEquals("xml", params.get("format")); + assertEquals("tokens", params.get("action")); + assertEquals("edit", params.get("type")); + + assertEquals("baz", result); + } + + @Test + public void fileExistsWithName_FileNotFound() throws Exception { + server.enqueue(new MockResponse().setBody(" ")); + + boolean result = testObject.fileExistsWithName("foo"); + + RecordedRequest request = assertBasicRequestParameters(server, "GET"); + Map params = parseQueryParams(request); + assertEquals("xml", params.get("format")); + assertEquals("query", params.get("action")); + assertEquals("imageinfo", params.get("prop")); + assertEquals("File:foo", params.get("titles")); + + assertFalse(result); + } + + + private RecordedRequest assertBasicRequestParameters(MockWebServer server, String method) throws InterruptedException { + RecordedRequest request = server.takeRequest(); + assertEquals("/", request.getRequestUrl().encodedPath()); + assertEquals(method, request.getMethod()); + assertEquals("Commons/" + BuildConfig.VERSION_NAME + " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE, request.getHeader("User-Agent")); + if ("POST".equals(method)) { + assertEquals("application/x-www-form-urlencoded", request.getHeader("Content-Type")); + } + return request; + } + + private Map parseQueryParams(RecordedRequest request) { + Map result = new HashMap<>(); + HttpUrl url = request.getRequestUrl(); + Set params = url.queryParameterNames(); + for (String name : params) { + result.put(name, url.queryParameter(name)); + } + return result; + } + + private Map parseBody(String body) throws UnsupportedEncodingException { + String[] props = body.split("&"); + Map result = new HashMap<>(); + for (String prop : props) { + String[] pair = prop.split("="); + result.put(pair[0], URLDecoder.decode(pair[1], "utf-8")); + } + return result; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 080aec51a..69bfd521d 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -63,7 +63,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .param("rememberMe", "1") .param("username", username) .param("password", password) - .param("logintoken", this.getLoginToken()) + .param("logintoken", getLoginToken()) .param("loginreturnurl", "https://commons.wikimedia.org") .post()); }